Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8168723
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: 数据库开发技术

2008-03-14 13:35:19

下载本文示例代码
一、概述
  1. 本系统为内部系统,帐户由管理员添加、管理;
  2. 分为两个组,User组和Boss组。Boss组的帐户可以发广播通知;
  3. 任意两个用户间可以互相通信;
  4. 数据库接口用DAO,网络通信用 CSocket+CSocketFile;

二、详细设计

1、数据库设计

本系统只是一个消息通信模型,这里的数据库设计比较简单。

ER图:


 

把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;
     }
}      
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“,最后点击 “完成”;
<2>、在CSuperServerView中添加下列成员:
 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;im_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;im_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;iAccept (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数据库维护操作函数: 
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。
下载本文示例代码
阅读(1349) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~