典型网络数据库系统软件设计
一、概述
- 本系统为内部系统,帐户由管理员添加、管理;
- 分为两个组,User组和Boss组。Boss组的帐户可以发广播通知;
- 任意两个用户间可以互相通信;
- 数据库接口用DAO,网络通信用 CSocket+CSocketFile;
二、详细设计
1、数据库设计
本系统只是一个消息通信模型,这里的数据库设计比较简单。
ER图:
User (No , Name ,Password ,G#) 候选键:No 外键:G# Group (G# , GroupName ,Demo ) 主键 :G#
2、消息格式设计
<1>、传送的消息共有5类------登录消息,验证返回消息,普通消息,用户列表消息,通知消息。定义一个枚举类型:
enum MSGTYPE {LOGIN , LOGINResponse , CHATTING , USERList , NOTICE};
<2>、定义消息类
class CMsg : public CObject { public: int m_eType; //枚举类型,记录消息类型 CString m_strMsg; //消息 CMsg(); virtual ~CMsg(); void Serialize(CArchive &ar); //消息类系列化函数,发送和接受消息时用。 };<3>、m_strMsg 为消息类中存放消息的成员,它的具体格式随着消息类型m_eType不同而不同。
m_eType | m_strMsg |
LOGIN | 呢称|密码 |
LOGINResponse | GOOD|欢迎!(BOSS) 或 FAILED|验证失败! |
CHATTING | 发给(来自)的用户名|消息内容 |
USERList | 呢称1|呢称2|…|呢称n|END |
NOTICE | ALL|消息内容 或 来自的用户|消息内容 |
m_strMsg中个内容用“|”隔开,用函数Decode(int n,CString strMsg) 获的相应的内容。
CString Decode(int n,CString strMsg) { int pos; CString str; for(int i=1;i<=n;i++) { pos=strMsg.Find ("|",0); if(pos<0) str=strMsg; else str=strMsg.Left (pos); strMsg=strMsg.Right(strMsg.GetLength ()-pos-1); } return str; }<4>、消息发送接收的序列化函数
void CMsg::Serialize (CArchive &ar) { if(ar.IsStoring()) { ar<<m_strMsg<<m_eType; } else { ar>>m_strMsg>>m_eType; } }3、通信协议设计
- 验证。客户端发送LOGIN消息,服务器回应 LOGINResponse消息;
- 通信。客户端发送CHATTING 或 NOTICE 消息,服务器端根据接收到的消息,发送CHATTING,NOTICE或 USERList 消息。
4、服务器设计
<1>、建立工程
- ①、用MFC AppWizard(exe) 新建一个“单个文档”的工程;
- ②、在Step 2 of 6中,选 “查看数据库不使用文件支持”,点击“Data Source..”按钮,然后在弹出的对话框中选 “DAO”类型,再浏览选择数据库文件Data.mdb. 按确定,再在弹出的窗口选User表;
- ③、点击Next到Step 4 of 6,选“windows Sockets”。网络功能支持;
- ④、按“NEXT“,最后点击 “完成”;
int m_iPort;//服务器端口 CSocketListen * m_pSocket; //监听套接字。 CSocketClient m_SocketClient[Max]; //跟客户端通信的套接字。 CArchive * m_pArOut; //发送消息时的序列化文档对象指针。 CArchive * m_pArIn; //接收消息时的序列化化文档对象指针。 CSocketFile * m_pSF; //套接字文件对象指针。 CMsg msg;//消息类对象 CString Decode(int n,CString strMsg); //消息的解码函数 void SendUserList(); bool CheckLogin(CSocketClient *pClient); void MyReceive(CSocketClient *pClient); void MyClose(CSocketClient *pClient); void MyAccept();<3>、CSuperServerView类的关键成员函数:
void CSuperServerView::MyReceive(CSocketClient *pClient) { m_pSF=new CSocketFile(pClient); m_pArIn=new CArchive(m_pSF,CArchive::load); msg.Serialize (*m_pArIn); int i; bool bOK=false; switch(msg.m_eType ) { case LOGIN: //处理用户登录。 { m_pSF=new CSocketFile(pClient); m_pArOut=new CArchive(m_pSF,CArchive::store); msg.m_eType =LOGINResponse; if(CheckLogin(pClient)) { if(!pClient->m_bBoss) { msg.m_strMsg="GOOD|欢迎!"; bOK=true; } else { msg.m_strMsg="GOOD|BOSS"; bOK=true; } else { msg.m_strMsg="FAILED|验证失败!"; } msg.Serialize (*m_pArOut); m_pArOut->Flush (); if(bOK) SendUserList(); break; } case CHATTING: //处理普通消息 { for(i=0;i<Max;i++) { if(m_SocketClient[i].m_bBusy) { if(m_SocketClient[i].m_strName ==Decode(1,msg.m_strMsg)) { m_pSF=new CSocketFile(&m_SocketClient[i]); m_pArOut=new CArchive(m_pSF,CArchive::store); msg.m_strMsg=pClient->m_strName+"|"+Decode(2,msg.m_strMsg); msg.Serialize (*m_pArOut); m_pArOut->Flush (); break; } } } break; } case NOTICE: //处理广播消息。 { msg.m_strMsg=pClient->m_strName+"|"+Decode(2,msg.m_strMsg); for(i=0;i<Max;i++) { if(m_SocketClient[i].m_bBusy && m_SocketClient[i].m_strName != pClient->m_strName) { m_pSF=new CSocketFile(&m_SocketClient[i]); m_pArOut=new CArchive(m_pSF,CArchive::store); msg.Serialize (*m_pArOut); m_pArOut->Flush (); } } break; } } } void CSuperServerView::MyAccept() { for(int i=0;i<Max;i++) { if(!m_SocketClient[i].m_bBusy) { m_pSocket->Accept (m_SocketClient[i]); m_SocketClient[i].GetView (this); break; } } } void CSuperServerView::OnStartServer() //开始服务 { m_pSocket=new CSocketListen(this); m_pSocket->Create (m_iPort,SOCK_STREAM); m_pSocket->Listen (); m_staState.SetWindowText("正在监听......"); } void CSuperServerView::OnStopServer() //关闭服务 { if(m_pSocket) m_pSocket->Close (); for(int i=0;i<Max;i++) { if(m_SocketClient[i].m_bBusy) { m_SocketClient[i].Close(); m_SocketClient[i].m_bBusy=false; } } m_staState.SetWindowText("服务关闭"); }数据库维护操作函数:
void CSuperServerView::OnButtonAdd() //添加帐户 { CAddDlg dlg; if(dlg.DoModal()==IDOK) { if(dlg.m_strName!="") { m_pSet->AddNew (); m_pSet->m_Name=dlg.m_strName; m_pSet->m_Password=dlg.m_strPwd; m_pSet->m_G_=dlg.m_iG; m_pSet->Update (); UpdateData(FALSE); } } } void CSuperServerView::OnButtonDel() //删除帐户 { m_pSet->Delete (); m_pSet->MoveNext (); if(m_pSet->IsEOF ()) m_pSet->MoveFirst(); UpdateData(FALSE); } void CSuperServerView::OnButtonModify() //修改帐户信息 { CAddDlg dlg; dlg.m_strName=m_pSet->m_Name; dlg.m_strPwd=m_pSet->m_Password; dlg.m_iG=m_pSet->m_G_; if(dlg.DoModal()==IDOK) { if(dlg.m_strName!="") { m_pSet->Edit (); m_pSet->m_Name=dlg.m_strName; m_pSet->m_Password=dlg.m_strPwd; m_pSet->m_G_=dlg.m_iG; m_pSet->Update (); UpdateData(FALSE); } } } void CSuperServerView::OnButtonFind() // 查找帐户 { CFindDLG dlg; if(dlg.DoModal()==IDOK) { if(dlg.m_strKey!="") { CString m_strName; UpdateData(TRUE); m_strName=dlg.m_strKey ; if(m_pSet->IsOpen ()) m_pSet->Close (); m_pSet->Open(AFX_DAO_USE_DEFAULT_TYPE,"SELECT * FROM user where Name=''"+m_strName+"''"); UpdateData(FALSE); } } } CSocketListen类中的接受事件函数OnAccept(int nErrorCode)。 void CSocketListen::OnAccept(int nErrorCode) { m_pView->MyAccept (); CSocket::OnAccept(nErrorCode); } CSocketClient类中的接收消息函数。 void CSocketClient::OnClose(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class m_pView->MyClose(this); CSocket::OnClose(nErrorCode); } CSocketClient类传递主窗口指针函数: void CSocketClient::GetView(CSuperServerView *pView) { m_pView=pView; }<4>、程序界面
5、客户端设计。
<1>、建立一个名为Client,基与对话框的应用程序,在Step 2 of 6中选Windows Sockts支持,
<2>、在CClientDlg中添加成员。
CString Decode(int n,CString strMsg); CMsg msg; CMySocket * m_pSocket; CArchive * m_pArOut; CArchive * m_pArIn; CSocketFile * m_pSF; void MyReceive();在CMySocket类中添加成员。
CClientDlg * m_pDlg; CMySocket(CClientDlg *pDlg);构造函数实现,获得指向主对话框的指针
CMySocket::CMySocket(CClientDlg *pDlg) { m_pDlg=pDlg; }<3>、关键函数
BOOL CClientDlg::OnInitDialog() { ... m_strHost="192.168.1.126"; m_iPort=1234; UpdateData(FALSE); GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(false); Expand(0); return TRUE; // return TRUE unless you set the focus to a control } void CClientDlg::OnLogin() //登录函数 { UpdateData(TRUE); GetDlgItem(IDC_BUTTON1)->SetWindowText("Wait"); m_pSocket=new CMySocket(this); m_pSocket->Create(); int nTry=3,n; do{ n=m_pSocket->Connect (m_strHost,m_iPort); }while(n!=1 && nTry--); if(n==1) { Sleep(2000); m_pSF=new CSocketFile(m_pSocket); m_pArOut=new CArchive(m_pSF,CArchive::store); m_pArIn=new CArchive(m_pSF,CArchive::load); msg.m_eType =LOGIN; msg.m_strMsg =m_strName+"|"+m_strPwd; msg.Serialize (*m_pArOut); m_pArOut->Flush(); } else { GetDlgItem(IDC_BUTTON1)->SetWindowText("Login"); GetDlgItem(IDC_BUTTON1)->EnableWindow(true); AfxMessageBox("网络原因,没连上服务器!"); } } void CClientDlg::MyReceive() //接收消息函数 { msg.Serialize (*m_pArIn); int i=0; CString str; switch(msg.m_eType ) { case LOGINResponse: str=Decode(1,msg.m_strMsg); if(str=="FAILED") { str=Decode(2,msg.m_strMsg); m_pSocket->Close(); AfxMessageBox(str); } else { Expand(true); GetDlgItem(IDC_BUTTON1)->SetWindowText("Login"); GetDlgItem(IDC_BUTTON1)->EnableWindow(false); GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(true); } break; case USERList: str=Decode(++i,msg.m_strMsg); m_ctrList.ResetContent(); while(str!="END") { if(str!=m_strName) m_ctrList.AddString(str); str=Decode(++i,msg.m_strMsg); } UpdateData(FALSE); break; case CHATTING: MessageBox(Decode(2,msg.m_strMsg),"来自"+Decode(1,msg.m_strMsg)+"的消息:"); break; case NOTICE: MessageBox(Decode(2,msg.m_strMsg),"来自"+Decode(1,msg.m_strMsg)+"的通知:"); break; } }CMySocket类中的事件函数OnReceive(int nErrorCode)。
void CMySocket::OnReceive(int nErrorCode) { m_pDlg->MyReceive (); //调用主窗口的接收函数 CSocket::OnReceive(nErrorCode); }<4>、程序界面
①、登录界面
②、登录成功(左为User组用户登录后界面,右为Boss组的)
③、发送消息
④、接收消息
三、结束语
这是一个基于网络和数据库的系统模型,有点学习的价值。程序还有一些功能没完善的地方,并且存在一些Bug。
评论