




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第2章 MFC Socket编程2.12.2MFC及其Socket类C/S模式下网络程序的Socket通信实例2.3Socket程序的互通2 . 1 MFC及其Socket类2.1.1 MFC简介MFC是很庞大的,以Visual C+ 6.0所带的MFC 4.21为例,已经包含了多达上百个类、结构和接口!如图2.1所示。2.1.1 MFC简介该层次结构可分为如下几种不同的类型: 应用程序框架; 图形绘制的绘制对象; 文件服务; 异常处理; 结构,如Lists、Arrays和Maps; Internet服务; OLE 2; 数据库;2.1.2 MFC中的Socket类1CAsyncSocket类
2、CAsyncSocket类是从MFC的根类CObject派生出来的,它在较低的级别上封装Windows Socket API,因此类中包含的大部分成员函数和底层Winsock API函数有很多相似之处,有的甚至连名称和参数叫法都是相同或相近的,CAsyncSocket类在MFC套接字类中的继承位置如图2.2所示。CAsyncSocket类的成员函数见表2.1。2.1.2 MFC中的Socket类2CSocket类及其相关类(1)CSocket类。CSocket类是从CAsyncSocket类派生而来的,它继承了CAsyncSocket对Windows Socket API的封装。与CAsync
3、Socket对象相比,CSocket对象代表了Windows Socket API的更高一级的抽象化。(2)CSocket与CArchive、CSocketFile类的配合使用。使用CSocket类编写网络程序,既可以使用如CAsyncSocket类网络程序一样的Send和Receive函数来收发信息,也可以与CSocketFile类和CArchive类一起管理数据的发送和接收。2 . 2 C/S模式下网络程序的Socket通信实例2.2.1 客户端服务器方式(C/S模式)1网络程序的通用体系结构客户端服务器方式所描述的是进程之间服务和被服务的关系。在图2.3中,主机A运行客户端程序而主机B运
4、行服务器程序。2.2.1 客户端服务器方式(C/S模式)2最简单的Socket通信流程这里先来考虑一个只有客户端向服务方发信息的单向通信,并且也只有客户端会主动提出断开连接的最简单的情形(相反过程的原理是一样的),其双方Socket之间的关系如图2.4所示。2.2.1 客户端服务器方式(C/S模式)由上面这个过程很容易得出对应的Socket通信流程,如图2.5所示。2.2.2 CAsyncSocket类编程基础1对象分析若要实现上节分析的Socket通信流程共需要三个套接字对象:客户端一个(称为“客户Socket”);服务器两个(一个用于监听,称为“监听Socket”;另一个用于接收客户发来的
5、信息,称为“服务Socket”)。这三个套接字对象对应三个Socket类,都继承自CAsyncSocket,分别为它们取名如表2.2所示(表中的类名也是后面程序中需要使用的)。Socket对象类 名客户SocketCClientSocket监听SocketCListenSocket服务SocketCServerSocket2.2.2 CAsyncSocket类编程基础2创建工程和套接字对象打开Visual Studio 2010环境,建立一个新的基于对话框的MFC项目,项目名称为ChatClient,向导的前几页设置同第1章程序,直到设置程序“高级功能”页,因为接下来要编写的是网络程序,使用套
6、接字,因此必须勾选“Windows套接字”复选框,如图2.6所示。2.2.2 CAsyncSocket类编程基础后面步骤中一律采用默认设置,直到完成工程的创建。工程创建后,由前述分析,还需要创建类名为CClientSocket的客户Socket对象,于是给工程添加类,选择菜单命令“项目”“添加类”,如图2.7所示。2.2.2 CAsyncSocket类编程基础在弹出的“添加类”对话框中选择“MFC类”项,单击“添加”按钮(如图2.8所示)。2.2.2 CAsyncSocket类编程基础在“MFC添加类向导”对话框中输入类名“CClientSocket”,由于要使用CAsyncSocket套接字
7、类编写程序,所以选择基类为“CAsyncSocket”,同时可以看出,向导还将自动为这个添加的类生成名为“ClientSocket.h”和“ClientSocket.cpp”的头文件和源文件,如图2.9所示。2.2.2 CAsyncSocket类编程基础单击“完成”按钮,就会在类视图中看到刚刚添加的类CClientSocket(如图2.10所示)。2.2.2 CAsyncSocket类编程基础3理清程序文件的组织结构1)客户端程序文件打开已经创建好的客户端工程,如图2.11所示,在解决方案资源管理器中可以看到工程所有的程序文件。2.2.2 CAsyncSocket类编程基础2)服务器的源文件同
8、理,服务器也对应这几种程序文件,打开服务器工程,可以看到它们(如图2.12所示)。2.2.2 CAsyncSocket类编程基础4用头文件和类对象将程序源文件连成有机整体要使客户端程序能够创建和控制本地的Socket,需在在客户端工程界面控制模块的头文件ChatClientDlg.h中添加如下两行代码:#include ClientSocket.h /使主界面程序能够访问Socket类的代码文件CClientSocket m_ClientSocket;/为了后面与服务器通信而定义的Socket成员变量以上两行代码的添加位置见图2.13中“/ADD”记号之间标出的部分。2.2.2 CAsyncS
9、ocket类编程基础相反地,若要使Socket能够访问到主界面上的控件,以便能够将自己的状况随时反映给主程序并在主界面上显示出来,也需要在Socket源文件ClientSocket.cpp中添加头文件声明:#include ChatClientDlg.h同理,若要使服务器程序能够创建和控制本地的Socket,也要在服务器工程界面控制模块的头文件ChatServerDlg.h中添加如下代码:#include ListenSocket.h /使主界面程序能够访问监听Socket类的代码文件#include ServerSocket.h /使主界面程序能够访问服务Socket类的代码文件CServe
10、rSocket m_ServerSocket;/为了后面与客户端通信而定义的Socket成员变量CListenSocket m_ListenSocket;/为了监听客户端的连接请求而定义的Socket成员变量2.2.2 CAsyncSocket类编程基础以上四行代码的添加位置见图2.14中“/ADD”记号之间标出的部分。2.2.2 CAsyncSocket类编程基础相反地,分别在监听套接字和服务套接字的源文件中添加声明。在ListenSocket.cpp中添加如下代码:#include ChatServerDlg.h在ServerSocket.cpp中添加如下代码:#include ChatS
11、erverDlg.h2.2.2 CAsyncSocket类编程基础5简单布置界面在客户端“资源视图”展开的目录树下双击Dialog文件夹下的第二个项目,转到用户界面设计工作区(如图2.15所示),先将设计页上的默认控件(一个静态文字标签和“确定”按钮)删除,留下“取消”按钮做程序的“退出”按钮之用。2.2.2 CAsyncSocket类编程基础在完成的客户端简化界面(如图2.16所示)上,包括IP地址控件、列表框各1个,文本框两个(一个用于接收用户输入端口号,另一个用于接收用户输入的待发送文本信息),4个按钮(其中“连接”、“断开”、“发送”3个按钮是我们添加的,“退出”按钮由原来的“取消”按
12、钮改变Caption属性得到)。2.2.2 CAsyncSocket类编程基础设计服务器的界面如图2.17所示,同样,为IP地址控件关联变量ServerIP,为文本框控件关联int型变量sPort,为列表框关联变量m_ListWords。2.2.2 CAsyncSocket类编程基础6添加核心代码到目前为止,已经有了一个程序框架,接下来添加核心源代码。先来回顾一下前面设计的那个最简单的Socket通信流程,如图2.18所示。2.2.2 CAsyncSocket类编程基础按照调用方式可将这些函数分为两大类。一类是由程序主动调用的,这类函数有Creat、Close、Connect、Send、Lis
13、ten、Accept,在源代码中很容易找到调用它们的程序语句;另一类是由系统的某个网络事件触发的,如OnConnect、OnAccept、OnReceive、OnClose,网络事件的触发时机和发生顺序不确定,因此不能事先编写流程,而在程序运行期间视实际情况触发,由系统自动调用,因此源代码中找不到调用这些函数的语句。2.2.2 CAsyncSocket类编程基础从图2.18已经标注了所用函数的通信流程图,可以进一步得出如图2.19所示的源代码组织框图。2.2.2 CAsyncSocket类编程基础7源代码完全剖析(1)客户端源码。客户端用户首先主动发起连接,以下是“连接”按钮的事件过程,位于C
14、hatClientDlg.cpp文件中:/连接服务器BYTE nFild4;CString sIP;UpdateData();m_ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);m_ClientSocket.Create();/创建客户端Socketm_ClientSocket.Connect(sIP,m_sPort);/发起连接请求2.2.2 CAsyncSocket类编程基础Create()函数原型如下:BOOL Create(UINT
15、nSocketport = 0, int nSocketType = SOCK_STREAM, long lEvent = FD_READ FD_WRITEFD_OOBFD_ACCEPTFD_CONNECTFD_CLOSE,LPCTSTR lpszSocketaddress = NULL);Connect()函数用于未连接的数据流或数据报套接字建立连接。其函数原型如下:BOOL Connect(LPCTSTR lpszHostAddress, UINT nHostPort);BOOL Connect(const SOCKADDR*lpSockAddr, int nSockAddrLen);2.
16、2.2 CAsyncSocket类编程基础客户端也可以随时主动断开通信连接,下面是“断开”按钮的事件过程:/断开与服务器的连接m_ClientSocket.Close();/关闭客户端Socketm_ListWords.AddString(从服务器断开);Close()函数用来关闭套接字并释放Socket描述符,其函数原型如下:virtual void Close();客户端可以向服务器发送信息,“发送”按钮的事件过程如下:/向服务器发信息UpdateData();m_ClientSocket.Send(m_sWords,m_sWords.GetLength();/发信息m_ListWords
17、.AddString(发送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);Send()函数通过数据报或者数据流向对方套接字发送数据,其函数原型如下:virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);2.2.2 CAsyncSocket类编程基础接下来要编写的是第二类(即网络事件响应)函数,源码中无法找到调用它们的语句,它们是由系统自动触发的,通过如图2.20所示的属性窗口来添加这类函数的代码。2.2.2 CAsyncSocket类编
18、程基础OnConnect()函数代码如下:/确认客户端是否成功连接到服务器if(nErrorCode)AfxMessageBox(连接失败,请您重试!);return;(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(连接成功);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()- 1);2.2.2 CAsync
19、Socket类编程基础(2)服务器源码(ChatServerDlg.cpp文件中)。“开始监听”按钮的事件过程如下:/监听开始,服务器等待连接请求的到来BYTE nFild4;CString sIP,sP;UpdateData();m_ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);sP.Format(%d,m_sPort);m_ListenSocket.Create(m_sPort,1,FD_ACCEPT,sIP); /创建服务器监听Soc
20、ketm_ListenSocket.Listen(1); /开始监听m_ListWords.AddString(在 + sIP + : + sP + 监听);Listen()函数用于侦听连接请求,原型如下:BOOL Listen(int nConnectionBacklog = 5);2.2.2 CAsyncSocket类编程基础“停止监听”按钮的事件过程如下:/停止监听m_ListenSocket.Close();/关闭服务器监听Socketm_ListWords.AddString(监听结束_);OnAccept()函数的代码如下:/ 接收客户端的连接请求Accept(CChatServe
21、rDlg* )(AfxGetApp()-m_pMainWnd)-m_ServerSocket);(CChatServerDlg* )(AfxGetApp()-m_pMainWnd)-m_ServerSocket.AsyncSelect(FD_READ|FD_CLOSE);(CChatServerDlg* )(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(接收了一个连接);(CChatServerDlg* )(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatServerDlg* )(AfxGe
22、tApp()-m_pMainWnd)-m_ListWords.GetCount() - 1);2.2.2 CAsyncSocket类编程基础Accept()函数接收一个套接字的连接请求,从连接请求队列中取出第一个连接,创建一个与这个套接字具有相同属性的套接字,并与参数rConnectedSocket相关联,原始的套接字依然保持打开并且侦听状态。函数原型如下:virtual BOOL Accept(CAsyncSocket& rConnectedSocket, SOCKADDR *lpSockAddr = NULL,int* lpSockAddrlen = NULL);2.2.2 CAsyncS
23、ocket类编程基础接收客户连接并创建了对应的套接字后,必须调用AsyncSelect()函数侦测发生在该套接字上的网络事件,选择感兴趣的网络事件来进行处理,表2.3列出了套接字可以侦测到的网络事件类型。事 件 标 记事 件FD_READ接收读准备好的通知FD_WRITE接收写准备好的通知FD_OOB接收带外数据到达的通知FD_ACCEPT接收等待连接成功的通知FD_CONNECT接收已连接好的通知FD_CLOSE接收套接字关闭的通知2.2.2 CAsyncSocket类编程基础OnReceive()函数代码(ServerSocket.cpp文件中)如下:/接收客户端发来的信息char szT
24、emp200;int n = Receive(szTemp,200);/接收信息szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);/显示信息(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(sTemp); (CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex( (CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCou
25、nt() - 1);OnClose()代码:/关闭与客户端的通信信道(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(客户已断开);(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()-1);Close();/关闭与客户端通信的Socket2.2.2 CAsyncSocket类编程基础8运行结果现在这个程序已
26、经具备了最简单的单向通信功能,客户端发送信息服务器可以收到,运行结果如图2.21所示。2.2.2 CAsyncSocket类编程基础9从单向通信到双向通信在服务器界面上添加一个编辑信息的文本框和一个发送按钮,如图2.21所示。2.2.2 CAsyncSocket类编程基础参照前面的方法,为服务器的“发送”按钮添加与客户端一样的事件过程。“发送”按钮的事件过程代码(在ChatServerDlg.cpp中)如下:UpdateData();m_ServerSocket.Send(m_sWords,m_sWords.GetLength();m_ListWords.AddString(发送: + m_s
27、Words);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);2.2.2 CAsyncSocket类编程基础与之对应,客户端当然也要编写响应函数用于接收服务器发来的信息,于是为客户端添加OnReceive方法,代码置于ClientSocket.cpp中。OnReceive()函数代码如下:char szTemp200;int n = Receive(szTemp,200);szTempn = 0;CString sTemp;sTemp.Format(收到:%s,szTemp);(CChatClientDlg *)(AfxGetApp()-
28、m_pMainWnd)-m_ListWords.AddString(sTemp);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex( (CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount() - 1);2.2.2 CAsyncSocket类编程基础在服务器界面上安放一个“断开”按钮,如图2.23所示。2.2.2 CAsyncSocket类编程基础为这个“断开”按钮编写与客户端“断开”按钮一样的事件过程代码:m_ServerSocket.C
29、lose();m_ListWords.AddString(与客户端断开);这里只不过将调用Close()函数的Socket对象由客户端的m_ClientSocket改成了服务器的m_ServerSocket,还有就是列表框里的界面状态提示信息改成了“与客户端断开”,这都无关紧要。接下来就是一样的原理,在客户端添加响应网络事件的函数代码,为客户端Socket重写网络事件响应函数OnClose,代码自动置于ClientSocket.cpp中:(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.AddString(服务器断开了);(CChat
30、ClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.SetTopIndex(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ListWords.GetCount()-1);Close();2.2.2 CAsyncSocket类编程基础10程序界面的优化先在界面上添加一些控件并重新设计布局,如图2.24和图2.25所示。2.2.2 CAsyncSocket类编程基础各控件关联的变量见表2.4和表2.5。2.2.2 CAsyncSocket类编程基础1)客户端在ChatClientDlg.cpp中,BOOL CC
31、hatClientDlg:OnInitDialog()函数的初始化代码如下:m_ButtonDisconnect.EnableWindow(false);m_ButtonClear.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);“清空”按钮事件过程代码如下:m_ListWords.ResetContent();/清空通信状态列表中的信息“断开”按钮事件过程代码如下:ServerIP.EnableWindow();ServerPort.EnableWindow();m_Bu
32、ttonConnect.EnableWindow();m_ButtonDisconnect.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);m_ButtonExit.EnableWindow();2.2.2 CAsyncSocket类编程基础“关于”按钮事件过程代码如下:CAboutDlg dlgAbout;dlgAbout.DoModal();/显示“关于”对话框在ClientSocket.cpp中,OnConnect()的界面控制代码如下:(CChatClientDlg
33、*)(AfxGetApp()-m_pMainWnd)-ServerIP.EnableWindow(false);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-ServerPort.EnableWindow(false);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonConnect.EnableWindow(false);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonDisconnect.EnableWindow();(CChatClientDlg*)
34、(AfxGetApp()-m_pMainWnd)-m_EditWords.EnableWindow();(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonSend.EnableWindow();(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonExit.EnableWindow(false);(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonClear.EnableWindow();2.2.2 CAsyncSocket类编程基础OnClose()
35、中的界面控制代码如下:(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-ServerIP.EnableWindow();(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-ServerPort.EnableWindow();(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonConnect.EnableWindow();(CChatClientDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonDisconnect.EnableWindow(false)
36、;(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_EditWords.EnableWindow(false);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ButtonSend.EnableWindow(false);(CChatClientDlg *)(AfxGetApp()-m_pMainWnd)-m_ButtonExit.EnableWindow();2.2.2 CAsyncSocket类编程基础2)服务器在ChatServerDlg.cpp中,BOOL CChatServerDlg:OnInitDia
37、log()函数的初始化代码如下:m_ButtonStopListen.EnableWindow(false);m_ButtonDisconnect.EnableWindow(false);m_ButtonClear.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);“开始监听”按钮事件过程代码如下:ServerIP.EnableWindow(false);ServerPort.EnableWindow(false);m_ButtonListen.EnableWindow(fa
38、lse);m_ButtonStopListen.EnableWindow();m_ButtonClear.EnableWindow();m_ButtonExit.EnableWindow(false);2.2.2 CAsyncSocket类编程基础“停止监听”按钮事件过程代码如下:ServerIP.EnableWindow();ServerPort.EnableWindow();m_ButtonListen.EnableWindow();m_ButtonStopListen.EnableWindow(false);m_ButtonExit.EnableWindow();“断开”按钮事件过程代码
39、如下:m_ButtonDisconnect.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);“清空”按钮事件过程代码如下:m_ListWords.ResetContent(); /清空状态历史列表中的信息“关于”按钮事件过程代码如下:CAboutDlg dlgAbout;dlgAbout.DoModal();/显示“关于”对话框2.2.2 CAsyncSocket类编程基础在ListenSocket.cpp中,OnAccept()函数的界面控制代码如下:(CChatServ
40、erDlg *)(AfxGetApp()-m_pMainWnd)-m_ButtonDisconnect.EnableWindow();(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_EditWords.EnableWindow();(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ButtonSend.EnableWindow();在ServerSocket.cpp中,OnClose()函数的界面控制代码如下:(CchatServerDlg*)(AfxGetApp()-m_pMainWnd)-m_ButtonDi
41、sconnect.EnableWindow(false);(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_EditWords.EnableWindow(false);(CChatServerDlg *)(AfxGetApp()-m_pMainWnd)-m_ButtonSend.EnableWindow(false);2.2.2 CAsyncSocket类编程基础这里截取上面的一小段代码来举一个例子,让读者对使用EnableWindow函数控制界面的方法有一个认识。例如,“开始监听”事件过程代码如下:(1)ServerIP.EnableWindow(fa
42、lse);/使IP地址控件不可用(2)ServerPort.EnableWindow(false);/使端口号固定无法修改(3)m_ButtonListen.EnableWindow(false);/使监听按钮失效(4)m_ButtonStopListen.EnableWindow();/使用户可以随时停止监听(5)m_ButtonClear.EnableWindow();/使清空按钮生效(6)m_ButtonExit.EnableWindow(false);/使退出按钮失效2.2.2 CAsyncSocket类编程基础服务器程序在用户按下“开始监听”按钮前后界面的变化对比如图2.26所示。2
43、.2.2 CAsyncSocket类编程基础“关于”事件过程代码如下:CAboutDlg dlgAbout;dlgAbout.DoModal();/显示“关于”对话框在资源视图的目录树Dialog子目录下的第一个项目IDD_ABOUTBOX就是“关于”对话框的ID,双击它可以进入 “关于服务器”对话框的设计界面(如图2.27所示)。2.2.2 CAsyncSocket类编程基础11程序完全演示分别开启客户端程序和服务器程序(如图2.28所示)。2.2.2 CAsyncSocket类编程基础在服务器输入IP地址和端口,单击“开始监听”按钮,于是服务器在这个地址上等待客户端来连接,历史记录栏里将自
44、动记下这一事件,如图2.29所示。2.2.2 CAsyncSocket类编程基础在客户端地址栏里输入与服务器一样的IP和端口,单击“连接”按钮,连上服务器,可以看到双方的状态栏里都反映了这次连接的情况(如图2.30所示)。2.2.2 CAsyncSocket类编程基础接下来双方就可以互相通信了,可以互发信息,信息内容会实时自动地显示在双方的通信记录列表中,并且用户还会发现:当某方的通信记录多到一定量时,列表框会自动以滚动条方式显示(如图2.31所示)。2.2.2 CAsyncSocket类编程基础通信过程中任何一方都可以主动断开连接(如图2.32所示)。2.2.2 CAsyncSocket类编
45、程基础客户端连接服务器失败时会弹出如图2.33所示的失败提示。2.2.3 CAsyncSocket类程序的指针实现1非指针程序的机制缺陷归纳起来2.2.2节的程序存在以下一些编程机制上的缺陷。Socket的创建、使用和销毁不够灵活,无法支持很多Socket动态参与通信过程。程序代码分散在各个不同的Socket模块中,不利于统一管理和维护。如果在Socket的代码中要访问和控制主对话框界面上的控件,则需要运用AfxGetApp()全局函数获取主窗口CWinApp类指针的方法,这意味着每一个在Socket中访问到主界面控件的语句前都要写上冗长的类似下面的这句代码:(CChatClientDlg*)
46、(AfxGetApp()-m_pMainWnd)-2.2.3 CAsyncSocket类程序的指针实现2用对话框指针机制实现的Socket程序1)建立工程。分别创建客户端和服务器工程,相关设置同2.2.2节,创建完成后在客户端和服务器工程中各添加一个基于CAsyncSocket类的MySocket类,如图2.34所示。这个类是用来给通信双方动态生成Socket对象服务的。2.2.3 CAsyncSocket类程序的指针实现2)用对话框指针机制组织新程序的框架。(1)客户端。在MySocket.h中添加如下代码:class CChatClientDlg; /为了能够在MySocket类中定义主对
47、话框类指针,首先对主对话框类进行前导声明CChatClientDlg * m_dlg; /在MySocket类中定义一个主对话框指针void GetDlg(CChatClientDlg * dlg); /获取主对话框指针的函数2.2.3 CAsyncSocket类程序的指针实现代码添加位置如图2.35所示。2.2.3 CAsyncSocket类程序的指针实现在ChatClientDlg.h中添加如下代码:#include MySocket.h /引用MySocket类的头文件,为下面定义一个MySocket类对象指针MySocket * m_ClientSocket;/定义一个MySocket
48、类对象指针,用于动态生成客户Socketvoid OnReceive();void OnClose();void OnConnect(); /一会儿要在主对话框代码中实现这三个函数的功能void SocketReset(); /套接字重置销毁函数,及时清理不再使用的Socket,避免指针悬空2.2.3 CAsyncSocket类程序的指针实现在ChatClientDlg.cpp中将MySocket类对象指针初始化为空,如图2.36所示。2.2.3 CAsyncSocket类程序的指针实现写出已声明的三个函数OnClose()、OnConnect()、OnReceive()的函数体,同时实现So
49、cketReset()函数,代码如下:void CChatClientDlg:SocketReset()/SocketReset函数实现 if(m_ClientSocket!=NULL) delete m_ClientSocket;m_ClientSocket=NULL; 2.2.3 CAsyncSocket类程序的指针实现在MySocket.cpp中实现获取主对话框指针的GetDlg()函数,并通过指针引用主对话框程序代码中的网络事件处理函数,代码如下:#include ChatClientDlg.hvoid MySocket:GetDlg(CChatClientDlg * dlg) /获得
50、窗口界面的指针 m_dlg=dlg; void MySocket:OnClose(int nErrorCode) / TODO: Add your specialized code here and/or call the base class m_dlg-OnClose(); CAsyncSocket:OnClose(nErrorCode);void MySocket:OnConnect(int nErrorCode) / TODO: Add your specialized code here and/or call the base class m_dlg-OnConnect(); CA
51、syncSocket:OnConnect(nErrorCode);void MySocket:OnReceive(int nErrorCode) / TODO: Add your specialized code here and/or call the base classm_dlg-OnReceive();CAsyncSocket:OnReceive(nErrorCode);2.2.3 CAsyncSocket类程序的指针实现采用与客户端类似的方法,在MySocket.h中添加如下代码:class CChatServerDlg;/首先对主对话框类进行前导声明CChatServerDlg *
52、 m_dlg;/定义主对话框指针void GetDlg(CChatServerDlg * dlg); /指针获取函数与客户端一样,系统自动为每个函数生成函数声明和函数体框架,另外,在ChatServerDlg.cpp中将MySocket类对象指针初始化为空。#include MySocket.h /引用MySocket类头文件,为后面定义动态Socket对象指针MySocket * m_ServerSocket; /MySocket类对象指针,用于动态生成与客户通信的SocketMySocket * m_ListenSocket; /定义MySocket类对象指针,用于动态生成监听Socket
53、void OnReceive();void OnClose();void OnAccept(); /要在主对话框代码中实现的服务器功能的三个函数void SocketReset(); /重置销毁不再使用的套接字2.2.3 CAsyncSocket类程序的指针实现在ChatServerDlg.cpp中写出OnAccept()、OnClose()、OnReceive()三个函数的函数体,为它们的实现代码预留出空间,同时实现SocketReset函数,代码如下:void CChatServerDlg:SocketReset() /SocketReset函数实现 if(m_ServerSocket !
54、= NULL)delete m_ServerSocket; /在此要销毁两个Socket,一个是与客户端通信的Socketm_ServerSocket = NULL; if(m_ListenSocket != NULL)delete m_ListenSocket;/还有一个是监听Socketm_ListenSocket = NULL;2.2.3 CAsyncSocket类程序的指针实现在MySocket.cpp中实现获取主对话框指针的GetDlg()函数,并通过指针引用主对话框的三个函数:#include ChatServerDlg.hvoid MySocket:GetDlg(CChatSer
55、verDlg *dlg) /获得窗口界面的指针m_dlg = dlg; void MySocket:OnClose(int nErrorCode) /TODO: 在此添加专用代码和/或调用基类 m_dlg-OnClose(); CAsyncSocket:OnClose(nErrorCode);void MySocket:OnReceive(int nErrorCode) /TODO: 在此添加专用代码和/或调用基类 m_dlg-OnReceive(); CAsyncSocket:OnReceive(nErrorCode);void MySocket:OnAccept(int nErrorCod
56、e) /TODO: 在此添加专用代码和/或调用基类if(m_dlg-m_ServerSocket = NULL)m_dlg-OnAccept(); CAsyncSocket:OnAccept(nErrorCode); 2.2.3 CAsyncSocket类程序的指针实现3)代码的复制、修改。(1)客户端。“连接”按钮事件过程代码如下:/初始化套接字,获取对话框指针if(!AfxSocketInit()MessageBox(WindowSocket initial failed!,Receive,MB_ICONSTOP);return;m_ClientSocket = new MySocket;
57、m_ClientSocket-GetDlg(this);/连接服务器BYTE nFild4;CString sIP;UpdateData();ServerIP.GetAddress(nFild0,nFild1,nFild2,nFild3);sIP.Format(%d.%d.%d.%d,nFild0,nFild1,nFild2,nFild3);m_ClientSocket-Create();/创建客户端Socketm_ClientSocket-Connect(sIP,sPort);/发起连接请求2.2.3 CAsyncSocket类程序的指针实现“断开”按钮事件过程代码如下:/断开与服务器的连接
58、m_ClientSocket-Close();/关闭客户端SocketSocketReset(); /避免指针悬空m_ListWords.AddString(从服务器断开);/界面完善m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);ServerIP.EnableWindow();ServerPort.EnableWindow();m_ButtonConnect.EnableWindow();m_ButtonDisconnect.EnableWindow(false);m_EditWords.EnableWindow(false);m_Bu
59、ttonSend.EnableWindow(false);m_ButtonExit.EnableWindow();“发送”按钮事件过程代码如下:UpdateData();m_ClientSocket-Send(m_sWords,m_sWords.GetLength();/向服务器发送信息m_ListWords.AddString(发送: + m_sWords);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);2.2.3 CAsyncSocket类程序的指针实现OnClose()函数代码如下:m_ListWords.AddString(服务
60、器断开了);m_ListWords.SetTopIndex(m_ListWords.GetCount() - 1);m_ClientSocket-Close();SocketReset();/避免指针悬空/界面完善ServerIP.EnableWindow();ServerPort.EnableWindow();m_ButtonConnect.EnableWindow();m_ButtonDisconnect.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);m_Button
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 公司房租租凭合同范本
- 劳动安全协议合同范本
- 包子店加盟签约合同范本
- 人工打草合同范本
- 冲孔加工销售合同范本
- 2024年河南省直第三人民医院招聘笔试真题
- 第14课《回忆我的母亲》教学设计 2024-2025学年统编版语文七年级上册
- 力工合同范例
- 中国铁建合同范本
- 包月工作合同范本
- 2024版消防设计质量问题案例分析手册建筑机电专业
- 《业财一体化实训教程-金蝶云星空V7.5》
- 工业机器人工作站系统组建课件 5.1康耐视is2000工业相机视觉识别操作
- 人教版二年级数学下册第一单元综合测评卷(含答案)
- 社区意识形态工作2025年度工作计划
- 2025年山东省济南广播电视台招聘30人历年管理单位笔试遴选500模拟题附带答案详解
- DG-TJ 08-2048-2024 民用建筑电气防火设计标准
- 2025年中智集团招聘笔试参考题库含答案解析
- 肝癌围手术期的护理
- 黑龙江省哈尔滨市南岗区2024-2025学年九年级上学期期末考试英语试题(含答案)
- 残疾人就业培训
评论
0/150
提交评论