MFC的网络编程.docx
- 文档编号:890890
- 上传时间:2023-04-30
- 格式:DOCX
- 页数:30
- 大小:188.90KB
MFC的网络编程.docx
《MFC的网络编程.docx》由会员分享,可在线阅读,更多相关《MFC的网络编程.docx(30页珍藏版)》请在冰点文库上搜索。
MFC的网络编程
MFC的网络编程
今天来八一八,MFC的SOCKET编程,利用CSocket实现一个基于TCP实现一个QQ聊天程序。
你会发现,MFC要比WIN32简单的多。
但是如果你不理解具体APIsocket基础知识,你可能会觉得有一点费解。
所以在开始之前我还是请大家先看看
在应用程序开始的时候,我们先应该初始话winSock库,所以便会用到下面的一个函数。
[cpp] viewplain copy
1.BOOL AfxSocketInit( WSADATA* lpwsaData = NULL ); //用来初始化Socket,用WSAStartup();来初始化,在应用程序结束时他会自动调用WSACleanup()
我们在开始编程之前,应该调用这个函数,对Socket进行初始化。
如果初始化成功返回非0,否则返回0.
可能人会问,这个函数加载的是那个版本的Socket库呢?
通过查看底层代码,我们发现,他加载的是1.1版本的Socket
注意:
这个函数只能在你自己应用程序的 CXXWinApp:
:
InitInstance 中初始化.在初始化前还要记得加入头文件Afxsock.h
我服务器端程序为 NetChatServer 所以我在的CNetChatServerApp:
:
InitInstance()中加入
/////////////////////////////////////////////////////////////////////////////////////////////////////CNetChatServerApp:
:
InitInstance()///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[cpp] viewplain copy
1. if(!
AfxSocketInit())
2.{
3. AfxMessageBox(_T("Socket 库初始化出错!
"));
4. return false;
5.}
m_iSocket是一个CServerSocket*的指针 ,CServerSocket类是一个我们自己的类我会在后面给出相应代码,他继承于CSocket类。
[cpp] viewplain copy
1. m_iSocket = new CServerSocket(); // 1.动态创建一个服务器Socket对象。
2. if(!
m_iSocket)
3. {
4.AfxMessageBox(_T("动态创建服务器套接字出错!
"));
5.return false;
6. }
接着创建套接字
[cpp] viewplain copy
1.if(!
m_iSocket->Create(8989))
2.{
3. AfxMessageBox(_T("创建套接字错误!
"));
4. m_iSocket->Close();
5. return false;
6.}
其中8989是指定的端口号,但是要注意在保存我们指定的8989端口前,这个端口是空闲的没有被其他进程所占用,那怎么查看端口是否被其他进程占用呢?
首先打开cmd键入netstat-aon
你会看到所有的TCP/UDP信息,但是由于太多了不好查看,所以。
我们再在最下面tasklist|find“8989”
现在我们看到我们没有找到任何和8989端口相关的东西,所以说明8989端口没有被占用。
创建了套接字以后按照win32的步骤我们就应该对bind端口。
但是MFC不这样,应为MFC的Create内部已经调用了bind,如下是MFC的底层代码
[cpp] viewplain copy
1.BOOL CAsyncSocket:
:
Create(UINT nSocketPort, int nSocketType,long lEvent, LPCTSTR lpszSocketAddress)
2.{
3. if (Socket(nSocketType, lEvent))
4. {
5. if (Bind(nSocketPort,lpszSocketAddress))//调用了bind
6. return TRUE;
7. int nResult = GetLastError();
8. Close();
9. WSASetLastError(nResult);
10. }
11. return FALSE;
12.}
所以我们不用在调用bind了,直接对套接字进行监听
[cpp] viewplain copy
1.if(!
m_iSocket->Listen())
2.{
3. AfxMessageBox(_T("监听失败!
"));
4. m_iSocket->Close();
5. return false;
6.}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
然后重载ExitInstance,退出时对进行清理
[cpp] viewplain copy
1.int CNetChatServerApp:
:
ExitInstance()
2.{
3.if(m_iSocket)
4.{
5.delete m_iSocket;
6.m_iSocket = NULL;
7.}
8.return CWinApp:
:
ExitInstance();
9.}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
下面来看下CServerSocket的具体实现
[cpp] viewplain copy
1.#pragma once
2.
3.#include "ClientSocket.h"
4.
5.class CServerSocket :
public CSocket
6.{
7.public:
8. CServerSocket();
9. virtual ~CServerSocket();
10.public :
11. CPtrList m_listSockets;//用来保存服务器与所有客户端连接成功后的ClientSocket
12.
13.
14.public :
15. virtual void OnAccept(int nErrorCode);
16.};
[cpp] viewplain copy
1.#include "stdafx.h"
2.#include "NetChatServer.h"
3.#include "ServerSocket.h"
4.
5.CServerSocket:
:
CServerSocket()
6.{
7.
8.}
9.
10.CServerSocket:
:
~CServerSocket()
11.{
12.
13.}
14.
15.void CServerSocket:
:
OnAccept(int nErrorCode)
16.{
17. //接受到一个连接请求
18. CClientSocket* theClientSock(0);
19. theClientSock = new CClientSocket(&m_listSockets);
20. if(!
theClientSock)
21. {
22. AfxMessageBox(_T("内存不足,客户连接服务器失败!
"));
23. return;
24. }
25. Accept(*theClientSock);
26. //加入list中便于管理
27. m_listSockets.AddTail(theClientSock);
28. CSocket:
:
OnAccept(nErrorCode);
29.}
我们可以看到在CServerSocket中又出现了一个CClientSocket的类,这个类和CServerSocket一样,也是派生于CSocket类,但是专门用于客户端的Socket。
在这里必须重载OnAccept(intnErrorCode)函数,这样CServerSocket才能接收到客户端的请求,并且必须在OnAccept中调用Accept()函数对连接请求进行响应。
在OnAccept()我们用一个List将ClientSocket指针保存,以便以后调用访问。
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
接着我们再来看看CClientSocket类
[cpp] viewplain copy
1.#pragma once
2.
3.#include "stdafx.h"
4./////////////////////////////////////////////////
5.///说明,该类用于和客户端建立通信的Socket
6./////////////////////////////////////////////////
7.
8.class CClientSocket :
public CSocket
9.{
10.public:
11. CClientSocket(CPtrList* pList);
12. virtual ~CClientSocket();
13.public:
14. CPtrList* m_pList;//保存服务器ClientSocket中List的东西,这个是中CServerSocket中传过来的
15. CString m_strName; //连接名称
16.public:
17. virtual void OnClose(int nErrorCode);
18. virtual void OnReceive(int nErrorCode);
19. void OnLogoIN(char* buff,int nlen);//处理登录消息
20. void OnMSGTranslate(char* buff,int nlen);//转发消息给其他聊天群
21. CString UpdateServerLog();//服务器端更新、记录日志
22. void UpdateAllUser(CString strUserInfo);//更新服务器端的在线人员列表
23.private:
24. BOOL WChar2MByte(LPCWSTR srcBuff, LPSTR destBuff, int nlen);//多字节的转换
25.};
可以看到我们重载了OnClose()、OnReceive()函数,这样当套接字关闭、有数据到达时,就会自动调用这两个函数,我们便可以在这两个函数中响应、处理事件。
由于本人使用的是VS2010,并且采用的Unicode编码,所以,经常要涉及Unicode转多字节的情况,于是就写了WChar2MByte()进行转换
[cpp] viewplain copy
1.#include "stdafx.h"
2.#include "NetChatServer.h"
3.#include "ClientSocket.h"
4.#include "Header.h"
5.#include "NetChatServerDlg.h"
6.
7.CClientSocket:
:
CClientSocket(CPtrList* pList)
8. :
m_pList(pList),m_strName(_T(""))
9.{
10.
11.}
12.
13.CClientSocket:
:
~CClientSocket()
14.{
15.}
16.
17./////////////////////////////////////////////////////////////////////
18. void CClientSocket:
:
OnReceive(int nErrorCode)
19. {
20. //有消息接收
21. //先得到信息头
22. HEADER head;
23. int nlen = sizeof HEADER;
24. char *pHead = NULL;
25. pHead = new char[nlen];
26. if(!
pHead)
27. {
28. TRACE0("CClientSocket:
:
OnReceive 内存不足!
");
29. return;
30. }
31. memset(pHead,0, sizeof(char)*nlen );
32. Receive(pHead,nlen);
33. head.type = ((LPHEADER)pHead)->type;
34. head.nContentLen = ((LPHEADER)pHead)->nContentLen;
35. delete pHead;
36. pHead = NULL;
37.
38. //再次接收,这次是数据类容
39. pHead = new char[head.nContentLen];
40. if(!
pHead)
41. {
42. TRACE0("CClientSocket:
:
OnRecive 内存不足!
");
43. return;
44. }
45. if( Receive(pHead, head.nContentLen)!
=head.nContentLen)
46. {
47. AfxMessageBox(_T("接收数据有误!
"));
48. delete pHead;
49. return;
50. }
51. ////////////根据消息类型,处理数据////////////////////
52. switch(head.type)
53. {
54. case MSG_LOGOIN:
55. OnLogoIN(pHead, head.nContentLen);
56. break;
57. case MSG_SEND:
58. OnMSGTranslate(pHead, head.nContentLen);
59. break;
60. default :
break;
61. }
62.
63. delete pHead;
64. CSocket:
:
OnReceive(nErrorCode);
65. }
66.
67. //关闭连接
68. void CClientSocket:
:
OnClose(int nErrorCode)
69. {
70. CTime time;
71. time = CTime:
:
GetCurrentTime();
72. CString strTime = time.Format("%Y-%m-%d %H:
%M:
%S ");
73. strTime = strTime + this->m_strName + _T(" 离开...\r\n");
74. ((CNetChatServerDlg*)theApp.GetMainWnd())->DisplayLog(strTime);
75. m_pList->RemoveAt(m_pList->Find(this));
76. //更改服务器在线名单
77. CString str1 = this->UpdateServerLog();
78. //通知客户端刷新在线名单
79. this->UpdateAllUser(str1);
80. this->Close();
81. //销毁该套接字
82. delete this;
83. CSocket:
:
OnClose(nErrorCode);
84. }
85.
86. //登录
87. void CClientSocket:
:
OnLogoIN(char* buff, int nlen)
88. {
89. //对得接收到的用户信息进行验证
90. //... (为了简化这步省略)
91. //登录成功
92. CTime time;
93. time = CTime:
:
GetCurrentTime();
94. CString strTime = time.Format("%Y-%m-%d %H:
%M:
%S ");
95.
96. CString strTemp(buff);
97. strTime = strTime + strTemp + _T(" 登录...\r\n");
98. //记录日志
99. ((CNetChatServerDlg*)theApp.GetMainWnd())->DisplayLog(strTime);
100. m_strName = strTemp;
101. //更新服务列表
102. CString str1 = this->UpdateServerLog();
103. //更新在线所有客服端
104. this->UpdateAllUser(str1);
105. }
106.
107.//转发消息
108. void CClientSocket:
:
OnMSGTranslate(char* buff, int nlen)
109. {
110. HEADER head;
111. head.type = MSG_SEND;
112. head.nContentLen = nlen;
113. POSITION ps = m_pList->GetHeadPosition();
114.
115. while(ps!
=NULL)
116. {
117. CClientSocket* pTemp = (CClientSocket*)m_pList->GetNext(ps);
118. pTemp->Send(&head,sizeof(HEADER));
119. pTemp->Send(buff, nlen);
120. }
121. }
122.
123.
124. BOOL CClientSocket:
:
WChar2MByte(L
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MFC 网络 编程