MFC的網(wǎng)絡編程_第1頁
MFC的網(wǎng)絡編程_第2頁
MFC的網(wǎng)絡編程_第3頁
MFC的網(wǎng)絡編程_第4頁
MFC的網(wǎng)絡編程_第5頁
已閱讀5頁,還剩16頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、MFC的網(wǎng)絡編程今天來八一八,MFC的SOCKET 編程,利用CSocket實現(xiàn)一個基于TCP實現(xiàn)一個QQ聊天程序。你會發(fā)現(xiàn),MFC要比WIN32 簡單的多。但是如果你不理解具體API socket基礎知識,你可能會覺得有一點費解。 所以在開始之前 我還是請大家先看看   在應用程序開始的時候,我們先應該初始話winSock 庫,所以便會用到下面的一個函數(shù)。cpp view plain copy1. BOOL AfxSocketInit( WSADATA* lpwsaData = NULL );

2、 /用來初始化Socket,用WSAStartup();來初始化,在應用程序結束時他會自動調(diào)用WSACleanup()  我們在開始編程之前,應該調(diào)用這個函數(shù),對Socket進行初始化。如果初始化成功返回非0 ,否則返回0.可能人會問,這個函數(shù)加載的是那個版本的Socket庫呢?通過查看底層代碼,我們發(fā)現(xiàn),他加載的是1.1版本的Socket注意:這個函數(shù)只能在你自己應用程序的 CXXWinApp:InitInstance 中初始化.在初始化前還要記得加入頭文件Afxsock.h我服務器端程序 為  NetChatServer 

3、0;所以我在的CNetChatServerApp:InitInstance()中加入/CNetChatServerApp:InitInstance()/cpp view plain copy1.   if(!AfxSocketInit()  2.   3.     AfxMessageBox(_T("Socket 庫初始化出錯!");  4.     return false;

4、  5.   m_iSocket 是一個 CServerSocket*的 指針  ,CServerSocket類是一個我們自己的類我會在后面給出相應代碼,他繼承于CSocket類。cpp view plain copy1.   m_iSocket = new CServerSocket(); / 1.動態(tài)創(chuàng)建一個服務器Socket對象。  2.     if(!m_iSocket) &#

5、160;3.       4. AfxMessageBox(_T("動態(tài)創(chuàng)建服務器套接字出錯!");  5. return false;  6.       接著創(chuàng)建套接字cpp view plain copy1. if(!m_iSocket->Create(8989)  2.   3.     Afx

6、MessageBox(_T("創(chuàng)建套接字錯誤!");  4.     m_iSocket->Close();  5.     return false;  6.   其中8989 是指定的端口號,但是要注意在保存我們指定的8989端口前,這個端口是空閑的沒有被其他進程所占用,那怎么查看端口是否被其他進程占用呢?首先打開cmd 鍵入 netstat -aon 你會看到所有的TCP/UDP 信息

7、 ,但是由于太多了不好查看,所以。我們再在最下面 tasklist|find “8989”現(xiàn)在我們看到 我們沒有找到任何 和8989端口相關的東西,所以說明8989端口沒有被占用。創(chuàng)建了套接字以后按照win32的步驟我們就應該 對bind端口。但是MFC 不這樣,應為MFC的Create內(nèi)部已經(jīng)調(diào)用了bind ,如下是MFC的底層代碼cpp view plain copy1. BOOL CAsyncSocket:Create(UINT nSocketPort, int nSocketType,long lEvent,

8、0;LPCTSTR lpszSocketAddress)  2.   3.       if (Socket(nSocketType, lEvent)  4.         5.           if (Bind(nSocketPort,lpszSock

9、etAddress)/調(diào)用了bind  6.                   return TRUE;  7.           int nResult = GetLastError();  8.  &#

10、160;        Close();  9.           WSASetLastError(nResult);  10.         11.       return FALSE;  12. &#

11、160; 所以 我們不用在調(diào)用bind 了,直接對套接字進行監(jiān)聽cpp view plain copy1. if(!m_iSocket->Listen()  2.   3.     AfxMessageBox(_T("監(jiān)聽失敗!");  4.     m_iSocket->Close();  5.     return fal

12、se;  6.   /然后重載ExitInstance,退出時對進行清理cpp view plain copy1. int CNetChatServerApp:ExitInstance()  2.   3. if(m_iSocket)  4.   5. delete m_iSocket;  6. m_iSocket = NULL;  7.   8. retu

13、rn CWinApp:ExitInstance();  9.   /下面 來看下CServerSocket的具體實現(xiàn)cpp view plain copy1. #pragma once  2.   3. #include "ClientSocket.h"  4.   5. class CServerSocket : public CSocket  

14、6.   7. public:  8.     CServerSocket();  9.     virtual CServerSocket();  10. public :   11.     CPtrList m_listSockets;/用來保存服務器與所有客戶端連接成功后的ClientSocket  12. &

15、#160; 13.   14. public :   15.     virtual void OnAccept(int nErrorCode);  16. ;  cpp view plain copy1. #include "stdafx.h"  2. #include "NetChatServer.h" 

16、 3. #include "ServerSocket.h"  4.   5. CServerSocket:CServerSocket()  6.   7.   8.   9.   10. CServerSocket:CServerSocket()  11.   12.   13.   14.   15. void

17、60;CServerSocket:OnAccept(int nErrorCode)  16.   17.     /接受到一個連接請求  18.     CClientSocket* theClientSock(0);  19.     theClientSock = new CClientSocket(&m_listSockets);&#

18、160; 20.     if(!theClientSock)  21.       22.         AfxMessageBox(_T("內(nèi)存不足,客戶連接服務器失敗!");  23.         return;  24.  &

19、#160;    25.     Accept(*theClientSock);  26.     /加入list中便于管理  27.     m_listSockets.AddTail(theClientSock);  28.     CSocket:OnAccept(nErrorCode);  29.  

20、 我們可以看到在CServerSocket中 又出現(xiàn)了一個CClientSocket的類,這個類和CServerSocket一樣,也是派生于CSocket類,但是專門用于客戶端的Socket。在這里必須重載OnAccept(int nErrorCode)函數(shù),這樣CServerSocket才能接收到客戶端的請求,并且必須在OnAccept中調(diào)用Accept()函數(shù)對連接請求進行響應。在OnAccept()我們用一個List 將ClientSocket指針保存,以便以后調(diào)用訪問。/接著 我們再來看看CClientSocket類cpp view plain copy1

21、. #pragma once  2.   3. #include "stdafx.h"  4. /  5. /說明,該類用于和客戶端建立通信的Socket  6. /  7.   8. class CClientSocket : public CSocket  9.   10. public:  11.  &

22、#160;  CClientSocket(CPtrList* pList);  12.     virtual CClientSocket();  13. public:  14.     CPtrList* m_pList;/保存服務器ClientSocket中List的東西,這個是中CServerSocket中傳過來的  15.     CSt

23、ring m_strName; /連接名稱  16. public:  17.     virtual void OnClose(int nErrorCode);   18.     virtual void OnReceive(int nErrorCode);  19.     void OnLo

24、goIN(char* buff,int nlen);/處理登錄消息  20.     void OnMSGTranslate(char* buff,int nlen);/轉(zhuǎn)發(fā)消息給其他聊天群  21.     CString UpdateServerLog();/服務器端更新、記錄日志  22.     void UpdateAllUser(CSt

25、ring strUserInfo);/更新服務器端的在線人員列表  23. private:  24.     BOOL WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen);/多字節(jié)的轉(zhuǎn)換  25. ;  可以看到 我們重載了OnClose()、OnReceive()函數(shù),這樣當套接字關閉、有數(shù)據(jù)到達時,就會自動調(diào)用這兩個函數(shù),我們便可以在這兩個函數(shù)

26、中響應、處理事件。由于本人使用的是VS2010,并且采用的Unicode編碼,所以,經(jīng)常要涉及Unicode轉(zhuǎn)多字節(jié)的情況,于是就寫了WChar2MByte()進行轉(zhuǎn)換cpp view plain copy1. #include "stdafx.h"  2. #include "NetChatServer.h"  3. #include "ClientSocket.h"  4. #include "Head

27、er.h"  5. #include "NetChatServerDlg.h"  6.   7. CClientSocket:CClientSocket(CPtrList* pList)  8.     :m_pList(pList),m_strName(_T("")  9.   10.   11.   12.  &#

28、160;13. CClientSocket:CClientSocket()  14.   15.   16.   17. /  18.  void CClientSocket:OnReceive(int nErrorCode)  19.    20.      /有消息接收  21.      /先

29、得到信息頭  22.      HEADER head;  23.      int nlen = sizeof HEADER;  24.      char *pHead = NULL;  25.      pHead =

30、60;new charnlen;  26.      if(!pHead)  27.        28.          TRACE0("CClientSocket:OnReceive 內(nèi)存不足!");  29.       

31、   return;  30.        31.      memset(pHead,0, sizeof(char)*nlen );  32.      Receive(pHead,nlen);  33.      head.type = (LP

32、HEADER)pHead)->type;  34.      head.nContentLen = (LPHEADER)pHead)->nContentLen;  35.      delete pHead;  36.      pHead = NULL;  37.   38. 

33、0;    /再次接收,這次是數(shù)據(jù)類容  39.      pHead = new charhead.nContentLen;  40.      if(!pHead)  41.        42.         &

34、#160;TRACE0("CClientSocket:OnRecive 內(nèi)存不足!");  43.          return;  44.        45.     if( Receive(pHead, head.nContentLen)!=head.nContentLen)  4

35、6.       47.         AfxMessageBox(_T("接收數(shù)據(jù)有誤!");  48.         delete pHead;  49.         return;  50. &#

36、160;     51.      /根據(jù)消息類型,處理數(shù)據(jù)/  52.      switch(head.type)  53.        54.      case MSG_LOGOIN:   55.    

37、60;     OnLogoIN(pHead, head.nContentLen);  56.          break;  57.      case MSG_SEND:   58.          OnMSGTransl

38、ate(pHead, head.nContentLen);  59.          break;  60.      default : break;  61.        62.   63.      delete

39、0;pHead;  64.      CSocket:OnReceive(nErrorCode);  65.    66.   67.  /關閉連接  68.  void CClientSocket:OnClose(int nErrorCode)  69.    70.       CTi

40、me time;   71.      time = CTime:GetCurrentTime();  72.      CString strTime = time.Format("%Y-%m-%d  %H:%M:%S  ");  73.      strT

41、ime = strTime + this->m_strName + _T("  離開.rn");  74.      (CNetChatServerDlg*)theApp.GetMainWnd()->DisplayLog(strTime);  75.      m_pList->RemoveAt(m_pList->Find(this)

42、;  76.      /更改服務器在線名單  77.      CString str1 = this->UpdateServerLog();  78.      /通知客戶端刷新在線名單  79.      this->UpdateAllUser(str1); &

43、#160;80.      this->Close();  81.      /銷毀該套接字  82.      delete this;  83.     CSocket:OnClose(nErrorCode);  84.    85.   86.  

44、/登錄  87.  void CClientSocket:OnLogoIN(char* buff, int nlen)  88.    89.      /對得接收到的用戶信息進行驗證  90.      /. (為了簡化這步省略)  91.      /登錄成功 &#

45、160;92.      CTime time;   93.      time = CTime:GetCurrentTime();  94.      CString strTime = time.Format("%Y-%m-%d %H:%M:%S  ");  95.

46、   96.      CString strTemp(buff);  97.      strTime = strTime + strTemp + _T("  登錄.rn");  98.     /記錄日志  99.     &

47、#160;(CNetChatServerDlg*)theApp.GetMainWnd()->DisplayLog(strTime);  100.      m_strName = strTemp;  101.      /更新服務列表  102.      CString str1 = this->UpdateServerLo

48、g();  103.      /更新在線所有客服端  104.      this->UpdateAllUser(str1);  105.    106.   107. /轉(zhuǎn)發(fā)消息  108.  void CClientSocket:OnMSGTranslate(char* buff, int nlen)

49、60; 109.    110.      HEADER head;  111.      head.type = MSG_SEND;  112.      head.nContentLen = nlen;  113.      POSITION&#

50、160;ps = m_pList->GetHeadPosition();  114.   115.      while(ps!=NULL)  116.        117.         CClientSocket* pTemp = (CClientSocket*)m_pLis

51、t->GetNext(ps);  118.         pTemp->Send(&head,sizeof(HEADER);  119.         pTemp->Send(buff, nlen);  120.        121.   

52、0;122.   123.   124.  BOOL CClientSocket:WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen)  125.    126.      int n = 0;  127.      n =

53、 WideCharToMultiByte(CP_OEMCP,0, srcBuff, -1, destBuff,0, 0, FALSE );  128.      if(n<nlen)  129.         return FALSE;  130.   131.    &

54、#160; WideCharToMultiByte(CP_OEMCP, 0, srcBuff, -1, destBuff, nlen, 0, FALSE);  132.   133.      return TRUE;  134.    135.   136.  /跟新所有在線用戶  137.  void&#

55、160;CClientSocket:UpdateAllUser(CString strUserInfo)  138.    139.       HEADER _head;  140.      _head.type = MSG_UPDATE;  141.      _head.nContentLen

56、0;= strUserInfo.GetLength()+1;  142.      char *pSend = new char_head.nContentLen;  143.      memset(pSend, 0, _head.nContentLen*sizeof(char);  144.     if( !WC

57、har2MByte(strUserInfo.GetBuffer(0), pSend, _head.nContentLen)  145.       146.         AfxMessageBox(_T("字符轉(zhuǎn)換失敗");  147.         delete pSend;

58、60; 148.         return;  149.       150.     POSITION ps = m_pList->GetHeadPosition();  151.     while(ps!=NULL)  152.    

59、;   153.          CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);  154.          /發(fā)送協(xié)議頭  155.         &#

60、160;pTemp->Send(char*)&_head, sizeof(_head);  156.          pTemp->Send(pSend,_head.nContentLen );  157.          158.        159. 

61、0;   delete pSend;  160.    161.    162.   163.  /跟新服務器在線名單    164.  / 返回在線用戶列表的String  165. CString CClientSocket:UpdateServerLog()  166.    167.  &#

62、160;   CString strUserInfo = _T("");  168.        169.      POSITION ps = m_pList->GetHeadPosition();  170.   171.      while(p

63、s!=NULL)  172.        173.          CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);  174.          strUserInfo += 

64、pTemp->m_strName + _T("#");  175.        176.     (CNetChatServerDlg*)theApp.GetMainWnd()->UpdateUserInfo(strUserInfo);  177.   178.     return strUserInfo; &#

65、160;179.    /在上面的代碼中 還涉及到一個HEADER struct 這是一個我們自定義的一個頭結構,相當于自定義的一個協(xié)議,不過這個很簡化。在這個協(xié)議里我們要指定我們本次要發(fā)送數(shù)據(jù)的type,既是我們發(fā)送的那種消息的數(shù)據(jù)。還有數(shù)據(jù)的長度。為了不浪費空間,我們選擇2次發(fā)送。每次給服務器發(fā)數(shù)據(jù)時都 先發(fā)送一個協(xié)議頭,然后再發(fā)送數(shù)據(jù)本身。其實也可以既不浪費空間,也只發(fā)送一次。但是那就在發(fā)送之前,對數(shù)據(jù)進行序列化。在接收端接收到數(shù)據(jù)后又反序列化。但是C+中并沒有提供相應的方法,所以我們要么自己寫,要么用第三方的庫類。但是這種方法代價比,我們分兩次發(fā)送代價高得

66、多,所以為了方便我們就分2次發(fā)送。/定義協(xié)議頭 因為直接要傳輸?shù)念惾葜杏胁淮_定長的的類容/為了避免浪費空間選擇分兩部分傳輸,故定義一個頭/#pragma once/自定義協(xié)議/const int MSG_LOGOIN = 0x01; /登錄const int MSG_SEND = 0x11;   /發(fā)送消息const int MSG_CLOSE = 0x02;  /退出const int MSG_UPDATE = 0x21; /更新信息#pragma pack(push,1)typedef struct tagHeaderint type ;/協(xié)議類型int nConten

67、tLen; /將要發(fā)送內(nèi)容的長度HEADER ,*LPHEADER;#pragma pack(pop)這里面涉及了一個字節(jié)對齊的知識,請查看 /到這里基本服務器端基本有關發(fā)送的框架全部搭建完畢,剩下的就是一些界面編程,比如什么顯示之類的工作,在這里我就不貼這些代碼,但是呢我會在最后給出整個工程的下載地址,下面我們就簡單看看客戶端的代碼/客戶端/客戶端相對來說要簡單的多,他只涉及一個CClientSocket,但是呢,這個類并不是和服務器端那個一樣的,只是名字相同而已。首先還是要初始化socket庫 不多說。位置和添加方法和客戶端一樣、接著創(chuàng)建客戶端的套接字、然后連接服務器。 

68、;cpp view plain copy1. if(!AfxSocketInit()  2.       3.         AfxMessageBox(_T("初始化Socket庫失敗!");  4.         return false;  5.  

69、     6.   7.     m_pSocket = new CClientSocket();  8.     if(!m_pSocket)  9.       10.         AfxMessageBox(_T("內(nèi)存

70、不足!");  11.         return false;  12.       13.   14.     if(!m_pSocket->Create()  15.       16.      

71、;   AfxMessageBox(_T("創(chuàng)建套接字失敗!");  17.         return false;  18.       19.   20.     CLogoInDlg* pLogoinDlg;/登錄對話框  21.   &#

72、160; pLogoinDlg = new CLogoInDlg();  22.       23.     if(pLogoinDlg->DoModal()=IDOK)/這里其實是點擊了推出的按鈕,只是ID我用的是IDOK的,沒有修改  24.       25.        &

73、#160;/不登錄  26.         delete pLogoinDlg;  27.         m_pSocket->Close();  28.         return false;  29.    &#

74、160;  30.     else  31.       32.         delete pLogoinDlg;  33.       (上面還有一個CLogoInDlg類,那是一個登錄對話框的類,在后面會給出他的部分代碼。)接著和服務器端一樣,重載ExitInstance();c

75、pp view plain copy1. int CNetChatClientApp:ExitInstance()  2.   3.     if(m_pSocket)  4.       5.         delete m_pSocket;  6.    

76、0;    m_pSocket = NULL;  7.       8.   9.     return CWinApp:ExitInstance();  10.   11.   12. CClientSocket* CNetChatClientApp:GetMainSocket() const &

77、#160;13.   14.     return m_pSocket;  15.   /然后 看看客戶端的CClientSocket的實現(xiàn)cpp view plain copy1. #pragma once  2.   3. class CClientSocket : public CSocket  4.   5. public:

78、60; 6.     CClientSocket();  7.     virtual CClientSocket();  8. public:  9.     virtual void OnReceive(int nErrorCode);/客戶端接收消息  10.     BOOL SendMSG

79、(LPSTR lpBuff, int nlen);/客戶端發(fā)送消息  11.     BOOL LogoIn(LPSTR lpBuff, int nlen);/客戶端登錄  12.     CString m_strUserName;/用戶姓名  13. ;  顯然我們必須重載OnReceive函數(shù),來處理接收到的數(shù)據(jù),其他函數(shù)是一些事件處理函數(shù),和說明一樣

80、cpp view plain copy1. #include "stdafx.h"  2. #include "NetChatClient.h"  3. #include "ClientSocket.h"  4. #include "Header.h"  5. #include "NetChatClientDlg.h"  6.

81、/ CClientSocket  7.   8. CClientSocket:CClientSocket()  9.     :m_strUserName(_T("")  10.   11.   12.   13.   14. CClientSocket:CClientSocket()  15.   16.  

82、0;17.   18.   19. void CClientSocket:OnReceive(int nErrorCode)  20.   21.     /首先接受head頭  22.     HEADER head   23.     char* pHead = NULL; 

83、 24.     pHead =  new charsizeof(head);  25.     memset(pHead, 0, sizeof(head);  26.     Receive(pHead, sizeof(head);  27.   28.     head.ty

84、pe =(LPHEADER)pHead)->type;  29.     head.nContentLen = (LPHEADER)pHead)->nContentLen;  30.     delete pHead;  31.     pHead = NULL;  32.   33.  

85、0;  char* pBuff = NULL;  34.     pBuff = new charhead.nContentLen;  35.     if(!pBuff)  36.       37.         AfxMessage

86、Box(_T("內(nèi)存不足!");  38.         return;  39.       40.     memset(pBuff, 0 , sizeof(char)*head.nContentLen);  41.     if(head.nContentLen

87、!=Receive(pBuff, head.nContentLen)  42.       43.         AfxMessageBox(_T("收到數(shù)據(jù)有誤!");  44.         delete pBuff;  45.    

88、60;    return;  46.       47.     CString strText(pBuff);  48.     switch(head.type)  49.       50.     case MSG_UPDATE:

89、   51.           52.             CString strText(pBuff);  53.             (CNetChatClientDlg*)(AfxGe

90、tApp()->GetMainWnd()->UpdateUserInfo(strText);  54.           55.         break;  56.     case MSG_SEND:  57.       

91、60;    58.             /顯示接收到的消息  59.             CString str(pBuff);  60.           

92、  (CNetChatClientDlg*)(AfxGetApp()->GetMainWnd()->UpdateText(str);  61.             break;  62.           63.     default: break;

93、  64.       65.   66.     delete pBuff;  67.     CSocket:OnReceive(nErrorCode);  68.   69.   70. BOOL CClientSocket:SendMSG(LPSTR lpBuff, int nle

94、n)  71.   72.     /生成協(xié)議頭  73.     HEADER head;  74.     head.type = MSG_SEND;  75.     head.nContentLen = nlen;  76.   77. &#

95、160;   if(Send(&head, sizeof(HEADER)=SOCKET_ERROR)  78.       79.         AfxMessageBox(_T("發(fā)送錯誤!");  80.         return FALSE;  

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論