程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 典型網絡數據庫系統軟件設計

典型網絡數據庫系統軟件設計

編輯:關於VC++

一、概述

本系統為內部系統,帳戶由管理員添加、管理;

分為兩個組,User組和Boss組。Boss組的帳戶可以發廣播通知;

任意兩個用戶間可以互相通信;

數據庫接口用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;
   }
   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“,最後點擊 “完成”;

<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;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。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved