




版權說明:本文檔由用戶提供并上傳,收益歸屬內(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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年儀表照明系統(tǒng)合作協(xié)議書
- 2024年中國石油高校畢業(yè)生招聘考試真題
- 2025年三次采油采出液絮凝劑項目合作計劃書
- 人教版一年級下冊語文期末復習計劃
- 2025年廢棄資源回收加工項目發(fā)展計劃
- 混凝土冬季施工的安全措施
- 生物技術研發(fā)課題中期報告范文
- 2024-2025管理人員崗前安全培訓考試試題(完美版)
- 2024-2025安全培訓考試試題含答案解析
- 25年企業(yè)員工崗前安全培訓考試試題帶答案解析
- 廣東省佛山2024年中考一模數(shù)學試卷(含答案)
- 北京市《配電室安全管理規(guī)范》(DB11T 527-2021)地方標準
- 特應性皮炎治療藥物應用管理專家共識2024版解讀
- 支氣管肺炎護理查房
- 上海市存志中學2024-2025學年中考一模英語試題含答案
- MTT 1114-2011 煤礦供電監(jiān)控系統(tǒng)通.用技術條件
- 貴州省遵義市2019年中考數(shù)學試卷【含答案】
- 飛機管路基礎知識課件講解
- 合肥安徽合肥經(jīng)開區(qū)擇優(yōu)招錄社區(qū)工作者20人筆試歷年典型考題及考點附答案解析
- JTS-T-272-1-2014沿海港口建設工程投資估算指標
- 中醫(yī)醫(yī)療技術手冊2013普及版
評論
0/150
提交評論