




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、MFC的网络编程今天来八一八,MFC的SOCKET 编程,利用CSocket实现一个基于TCP实现一个QQ聊天程序。你会发现,MFC要比WIN32 简单的多。但是如果你不理解具体API socket基础知识,你可能会觉得有一点费解。 所以在开始之前 我还是请大家先看看 在应用程序开始的时候,我们先应该初始话winSock 库,所以便会用到下面的一个函数。cpp view plain copy1. BOOL AfxSocketInit( WSADATA* lpwsaData = NULL );
2、 /用来初始化Socket,用WSAStartup();来初始化,在应用程序结束时他会自动调用WSACleanup() 我们在开始编程之前,应该调用这个函数,对Socket进行初始化。如果初始化成功返回非0 ,否则返回0.可能人会问,这个函数加载的是那个版本的Socket库呢?通过查看底层代码,我们发现,他加载的是1.1版本的Socket注意:这个函数只能在你自己应用程序的 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.动态创建一个服务器Socket对象。 2. if(!m_iSocket)
5、160;3. 4. AfxMessageBox(_T("动态创建服务器套接字出错!"); 5. return false; 6. 接着创建套接字cpp view plain copy1. if(!m_iSocket->Create(8989) 2. 3. Afx
6、MessageBox(_T("创建套接字错误!"); 4. m_iSocket->Close(); 5. return false; 6. 其中8989 是指定的端口号,但是要注意在保存我们指定的8989端口前,这个端口是空闲的没有被其他进程所占用,那怎么查看端口是否被其他进程占用呢?首先打开cmd 键入 netstat -aon 你会看到所有的TCP/UDP 信息
7、 ,但是由于太多了不好查看,所以。我们再在最下面 tasklist|find “8989”现在我们看到 我们没有找到任何 和8989端口相关的东西,所以说明8989端口没有被占用。创建了套接字以后按照win32的步骤我们就应该 对bind端口。但是MFC 不这样,应为MFC的Create内部已经调用了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)/调用了bind 6. return TRUE; 7. int nResult = GetLastError(); 8.
10、160; Close(); 9. WSASetLastError(nResult); 10. 11. return FALSE; 12.
11、160; 所以 我们不用在调用bind 了,直接对套接字进行监听cpp view plain copy1. if(!m_iSocket->Listen() 2. 3. AfxMessageBox(_T("监听失败!"); 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的具体实现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("内存不足,客户连接服务器失败!"); 23. return; 24. &
19、#160; 25. Accept(*theClientSock); 26. /加入list中便于管理 27. m_listSockets.AddTail(theClientSock); 28. CSocket:OnAccept(nErrorCode); 29.
20、 我们可以看到在CServerSocket中 又出现了一个CClientSocket的类,这个类和CServerSocket一样,也是派生于CSocket类,但是专门用于客户端的Socket。在这里必须重载OnAccept(int nErrorCode)函数,这样CServerSocket才能接收到客户端的请求,并且必须在OnAccept中调用Accept()函数对连接请求进行响应。在OnAccept()我们用一个List 将ClientSocket指针保存,以便以后调用访问。/接着 我们再来看看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);/转发消息给其他聊天群 21. CString UpdateServerLog();/服务器端更新、记录日志 22. void UpdateAllUser(CSt
25、ring strUserInfo);/更新服务器端的在线人员列表 23. private: 24. BOOL WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen);/多字节的转换 25. ; 可以看到 我们重载了OnClose()、OnReceive()函数,这样当套接字关闭、有数据到达时,就会自动调用这两个函数,我们便可以在这两个函数
26、中响应、处理事件。由于本人使用的是VS2010,并且采用的Unicode编码,所以,经常要涉及Unicode转多字节的情况,于是就写了WChar2MByte()进行转换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 内存不足!"); 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; /再次接收,这次是数据类容 39. pHead = new charhead.nContentLen; 40. if(!pHead) 41. 42. &
34、#160;TRACE0("CClientSocket:OnRecive 内存不足!"); 43. return; 44. 45. if( Receive(pHead, head.nContentLen)!=head.nContentLen) 4
35、6. 47. AfxMessageBox(_T("接收数据有误!"); 48. delete pHead; 49. return; 50.
36、160; 51. /根据消息类型,处理数据/ 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. /转发消息 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("字符转换失败"); 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. /发送协议头 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 这是一个我们自定义的一个头结构,相当于自定义的一个协议,不过这个很简化。在这个协议里我们要指定我们本次要发送数据的type,既是我们发送的那种消息的数据。还有数据的长度。为了不浪费空间,我们选择2次发送。每次给服务器发数据时都 先发送一个协议头,然后再发送数据本身。其实也可以既不浪费空间,也只发送一次。但是那就在发送之前,对数据进行序列化。在接收端接收到数据后又反序列化。但是C+中并没有提供相应的方法,所以我们要么自己写,要么用第三方的库类。但是这种方法代价比,我们分两次发送代价高得
66、多,所以为了方便我们就分2次发送。/定义协议头 因为直接要传输的类容中有不确定长的的类容/为了避免浪费空间选择分两部分传输,故定义一个头/#pragma once/自定义协议/const int MSG_LOGOIN = 0x01; /登录const int MSG_SEND = 0x11; /发送消息const int MSG_CLOSE = 0x02; /退出const int MSG_UPDATE = 0x21; /更新信息#pragma pack(push,1)typedef struct tagHeaderint type ;/协议类型int nConten
67、tLen; /将要发送内容的长度HEADER ,*LPHEADER;#pragma pack(pop)这里面涉及了一个字节对齐的知识,请查看 /到这里基本服务器端基本有关发送的框架全部搭建完毕,剩下的就是一些界面编程,比如什么显示之类的工作,在这里我就不贴这些代码,但是呢我会在最后给出整个工程的下载地址,下面我们就简单看看客户端的代码/客户端/客户端相对来说要简单的多,他只涉及一个CClientSocket,但是呢,这个类并不是和服务器端那个一样的,只是名字相同而已。首先还是要初始化socket库 不多说。位置和添加方法和客户端一样、接着创建客户端的套接字、然后连接服务器。
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("内存
70、不足!"); 11. return false; 12. 13. 14. if(!m_pSocket->Create() 15. 16.
71、; AfxMessageBox(_T("创建套接字失败!"); 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的实现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);/客户端发送消息 11. BOOL LogoIn(LPSTR lpBuff, int nlen);/客户端登录 12. CString m_strUserName;/用户姓名 13. ; 显然我们必须重载OnReceive函数,来处理接收到的数据,其他函数是一些事件处理函数,和说明一样
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("内存不足!"); 38. return; 39. 40. memset(pBuff, 0 , sizeof(char)*head.nContentLen); 41. if(head.nContentLen
87、!=Receive(pBuff, head.nContentLen) 42. 43. AfxMessageBox(_T("收到数据有误!"); 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. /生成协议头 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("发送错误!"); 80. return FALSE;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 巴彦淖尔市中医院消化内科内镜技师晋升高级技师考核试题
- 中国牛冻精项目商业计划书
- 石家庄市人民医院多胎妊娠减胎术技术准入考核
- 北京市人民医院心衰中心规范化管理年度审核
- 2025年中国三氧化铬项目投资计划书
- 巴彦淖尔市中医院肾上腺意外瘤诊疗策略考核
- 牡丹江市中医院血液病相关免疫学检测项目选择考核
- 2025年中国去甲氧基醉椒素项目创业计划书
- 忻州市中医院感染性疾病影像考核
- 长治市人民医院罕见病诊疗体系建设考核
- GB/T 40335-2021无损检测泄漏检测示踪气体方法
- GB/T 16785-2012术语工作概念和术语的协调
- 广州市材料进场检测相关法律法规、检测项目及取样规则
- LENZE-9300EP简明调试汇总课件
- 工伤预防培训-课件
- 公司技术响应表
- 教科版五年级(上)科学2.2地球的结构(课件)
- DBJ∕T13-358-2021 福建省城镇供水基础数据采集与管理技术标准
- 齐鲁医学UCLA肩关节评分系统
- Q∕SY 05064-2018 油气管道动火规范
- 财务收支月报表excel模板
评论
0/150
提交评论