如何用MFC实现网络编程? 如何用MFC实现网络编程? 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 设为首页 广告刊例 经销商名单 技术讨论区 请输入关键词 首页 新闻聚焦 经典文章 疑难诊所 春秋茶楼 你目前所在的位置:程序春秋>>经典文章>>封面专题 如何使用MFC实现网络编程 -------------------------------------------------------------------------------- 作者:传老鹰 随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于C++语言对底层操作的优越性,许多文章都曾经介绍过用VC++进行Socket编程的方法。但由于都是直接利用动态连接库wsock32.dll进 行操作,实现比较繁琐。其实,VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用他来实现Socket编程,是非常方便的。 本文将用一个Echo例程来介绍CAsyncSocket类的用法。 一. 客户端 1. 创建一个Dialog Based项目:CSockClient。 2. 设计对话框 去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中为CCSockClientDlg类添加变量。 Control ID Type Member IDC_EDITMSG CEdit m_MSG IDC_LISTMSG ClistBox m_MSGS 3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。 由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:以Public方式继承CAsyncSocket类,生成新类MySock;为MySock类添加虚函数OnReceive、OnConnect、OnSend。 4. 在MySock.ccp中添加以下代码 #include "CSockClient.h" #include "CSockClientDlg.h" 5. 在MySock.h中添加以下代码 public: BOOL m_bConnected; //是否连接 UINT m_nLength; //消息长度 char m_szBuffer[4096]; //消息缓冲区 6. 在MySock.ccp中重载各函数 MySock::MySock() { m_nLength=0; memset(m_szBuffer,0,sizeof(m_szBuffer)); m_bConnected=FALSE; } MySock::~MySock() { //关闭套接字 if(m_hSocket!=INVALID_SOCKET) Close(); } void MySock::OnReceive(int nErrorCode) { m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); //下面两行代码用来获取对话框指针 CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp(); CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd; pDlg- >m_MSGS.InsertString(0,m_szBuffer); memset(m_szBuffer,0,sizeof(m_szBuffer)); CAsyncSocket::OnReceive(nErrorCode); } void MySock::OnSend(int nErrorCode) { Send(m_szBuffer,m_nLength,0); m_nLength=0; memset(m_szBuffer,0,sizeof(m_szBuffer)); //继续提请一个“读”的网络事件,接收Server消息 AsyncSelect(FD_READ); CAsyncSocket::OnSend(nErrorCode); } void MySock::OnConnect(int nErrorCode) { if (nErrorCode==0) { m_bConnected=TRUE; CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp(); CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd; memcpy(m_szBuffer,"Connected to ",13); strncat(m_szBuffer,pDlg- >m_szServerAdr, sizeof(pDlg- >m_szServerAdr)); pDlg- >m_MSGS.InsertString(0,m_szBuffer); AsyncSelect(FD_READ); ////提请一个“读”的网络事件,准备接收 } CAsyncSocket::OnConnect(nErrorCode); } 7. 新建对话框IDD_Addr,用来输入IP地址和Port;生成新类CAddrDlg。增加两个Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中为CAddrDlg类添加变量。 Control ID Type Member IDC_Addr CString m_Addr IDC_Port Int m_Port 8. 在CSockClientDlg.ccp中添加代码: #include "AddrDlg.h" protected: int TryCount; MySock m_clientSocket; UINT m_szPort; public: char m_szServerAdr[256]; 9. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码: void CCSockClientDlg::OnConnect() { m_clientSocket.ShutDown(2); m_clientSocket.m_hSocket=INVALID_SOCKET; m_clientSocket.m_bConnected=FALSE; CAddrDlg m_Dlg; //默认端口1088 m_Dlg.m_Port=1088; if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty()) { memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr)); m_szPort=m_Dlg.m_Port; //建立计时器,每1秒尝试连接一次,直到连上或TryCount>10 SetTimer(1,1000,NULL); TryCount=0; } } 10. 添加Windows消息WM_TIMER响应函数OnTimer void CCSockClientDlg::OnTimer(UINT nIDEvent) { if (m_clientSocket.m_hSocket==INVALID_SOCKET) { BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT); if(!bFlag) { AfxMessageBox("Socket Error!"); m_clientSocket.Close(); PostQuitMessage(0); return; } } m_clientSocket.Connect(m_szServerAdr,m_szPort); TryCount++; if (TryCount >=10 || m_clientSocket.m_bConnected) { KillTimer(1); if (TryCount >=10) AfxMessageBox("Connect Failed!"); return; } CDialog::OnTimer(nIDEvent); } 11. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“发送”按钮,添加以下代码: void CCSockClientDlg::OnSend() { if (m_clientSocket.m_bConnected) { m_clientSocket.m_nLength=m_MSG.GetWindowText (m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer)); m_clientSocket.AsyncSelect(FD_WRITE); m_MSG.SetWindowText(""); } } 12. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码: void CCSockClientDlg::OnExit() { //关闭Socket m_clientSocket.ShutDown(2); //关闭对话框 EndDialog(0); } 12.运行此项目,连接时输入主机名或IP均可,CAsyncSocket类会自动处理。 二. 服务端 Server端的编程与Client端的类似,下面主要介绍他的Listen及Accept函数。 1. 建立一个CNewSocket类,重载CAsyncSocket类的OnReceive、OnSend函数,如何进行信息的显示和发送可以参考Client程序。本例中采用将收到信息原封不动发回的方法来实现Echo功能,代码如下: CNewSocket::OnReceive(int nErrorCOde) { m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); // 直接转发消息 AsyncSelect(FD_WRITE); } CNewSocket::OnSend(int nErrorCode) { Send(m_szBuffer,m_nLength,0); } 2. 建立一个CMyServerSocket类,重载CAsyncSocket类的OnAccept函数代码如下 在MyServerSocket.h中声明变量 public:: CNewSocket* m_pSocket; void CMyServerSocket::OnAccept(int nErrorCode) { //侦听到连接请求,调用Accept函数 CNewSocket* pSocket = new CNewSocket(); if (Accept(*pSocket)) { pSocket- >AsyncSelect(FD_READ); m_pSocket=pSocket; } else delete pSocket; } 3. 为对话框添加一个“侦听”按钮,添加如下代码: 在CsockServerDlg.ccp中声明变量 public: CMyServerSocket m_srvrSocket; void CCSockServerDlg::OnListen() { if (m_srvrSocket.m_hSocket==INVALID_SOCKET) { BOOL bFlag=m_srvrSocket.Create (UserPort,SOCK_STREAM,FD_ACCEPT); if (!bFlag) { AfxMessageBox(“Socket Error!”); M_srvrSocket.Close(); PostQuitMessage(0); Return; } } //“侦听”成功,等待连接请求 if (!m_srvrSocket。Listen(1)) { int nErrorCode = m_srvrSocket.GetLastError(); if (nError!=WSAEWOULDBLOCK) { AfxMessageBox(“Socket Error!”); M_srvrSocket.Close(); 设为首页 广告刊例 经销商名单 技术讨论区 请输入关键词 首页 新闻聚焦 经典文章 疑难诊所 春秋茶楼 你目前所在的位置:程序春秋>>经典文章>>封面专题 如何使用MFC实现网络编程 -------------------------------------------------------------------------------- 作者:传老鹰 随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于C++语言对底层操作的优越性,许多文章都曾经介绍过用VC++进行Socket编程的方法。但由于都是直接利用动态连接库wsock32.dll进 行操作,实现比较繁琐。其实,VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用他来实现Socket编程,是非常方便的。 本文将用一个Echo例程来介绍CAsyncSocket类的用法。 一. 客户端 1. 创建一个Dialog Based项目:CSockClient。 2. 设计对话框 去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中为CCSockClientDlg类添加变量。 Control ID Type Member IDC_EDITMSG CEdit m_MSG IDC_LISTMSG ClistBox m_MSGS 3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。 由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:以Public方式继承CAsyncSocket类,生成新类MySock;为MySock类添加虚函数OnReceive、OnConnect、OnSend。 4. 在MySock.ccp中添加以下代码 #include "CSockClient.h" #include "CSockClientDlg.h" 5. 在MySock.h中添加以下代码 public: BOOL m_bConnected; //是否连接 UINT m_nLength; //消息长度 char m_szBuffer[4096]; //消息缓冲区 6. 在MySock.ccp中重载各函数 MySock::MySock() { m_nLength=0; memset(m_szBuffer,0,sizeof(m_szBuffer)); m_bConnected=FALSE; } MySock::~MySock() { //关闭套接字 if(m_hSocket!=INVALID_SOCKET) Close(); } void MySock::OnReceive(int nErrorCode) { m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); //下面两行代码用来获取对话框指针 CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp(); CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd; pDlg- >m_MSGS.InsertString(0,m_szBuffer); memset(m_szBuffer,0,sizeof(m_szBuffer)); CAsyncSocket::OnReceive(nErrorCode); } void MySock::OnSend(int nErrorCode) { Send(m_szBuffer,m_nLength,0); m_nLength=0; memset(m_szBuffer,0,sizeof(m_szBuffer)); //继续提请一个“读”的网络事件,接收Server消息 AsyncSelect(FD_READ); CAsyncSocket::OnSend(nErrorCode); } void MySock::OnConnect(int nErrorCode) { if (nErrorCode==0) { m_bConnected=TRUE; CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp(); CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd; memcpy(m_szBuffer,"Connected to ",13); strncat(m_szBuffer,pDlg- >m_szServerAdr, sizeof(pDlg- >m_szServerAdr)); pDlg- >m_MSGS.InsertString(0,m_szBuffer); AsyncSelect(FD_READ); ////提请一个“读”的网络事件,准备接收 } CAsyncSocket::OnConnect(nErrorCode); } 7. 新建对话框IDD_Addr,用来输入IP地址和Port;生成新类CAddrDlg。增加两个Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中为CAddrDlg类添加变量。 Control ID Type Member IDC_Addr CString m_Addr IDC_Port Int m_Port 8. 在CSockClientDlg.ccp中添加代码: #include "AddrDlg.h" protected: int TryCount; MySock m_clientSocket; UINT m_szPort; public: char m_szServerAdr[256]; 9. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码: void CCSockClientDlg::OnConnect() { m_clientSocket.ShutDown(2); m_clientSocket.m_hSocket=INVALID_SOCKET; m_clientSocket.m_bConnected=FALSE; CAddrDlg m_Dlg; //默认端口1088 m_Dlg.m_Port=1088; if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty()) { memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr)); m_szPort=m_Dlg.m_Port; //建立计时器,每1秒尝试连接一次,直到连上或TryCount>10 SetTimer(1,1000,NULL); TryCount=0; } } 10. 添加Windows消息WM_TIMER响应函数OnTimer void CCSockClientDlg::OnTimer(UINT nIDEvent) { if (m_clientSocket.m_hSocket==INVALID_SOCKET) { BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT); if(!bFlag) { AfxMessageBox("Socket Error!"); m_clientSocket.Close(); PostQuitMessage(0); return; } } m_clientSocket.Connect(m_szServerAdr,m_szPort); TryCount++; if (TryCount >=10 || m_clientSocket.m_bConnected) { KillTimer(1); if (TryCount >=10) AfxMessageBox("Connect Failed!"); return; } CDialog::OnTimer(nIDEvent); } 11. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“发送”按钮,添加以下代码: void CCSockClientDlg::OnSend() { if (m_clientSocket.m_bConnected) { m_clientSocket.m_nLength=m_MSG.GetWindowText (m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer)); m_clientSocket.AsyncSelect(FD_WRITE); m_MSG.SetWindowText(""); } } 12. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码: void CCSockClientDlg::OnExit() { //关闭Socket m_clientSocket.ShutDown(2); //关闭对话框 EndDialog(0); } 12.运行此项目,连接时输入主机名或IP均可,CAsyncSocket类会自动处理。 二. 服务端 Server端的编程与Client端的类似,下面主要介绍他的Listen及Accept函数。 1. 建立一个CNewSocket类,重载CAsyncSocket类的OnReceive、OnSend函数,如何进行信息的显示和发送可以参考Client程序。本例中采用将收到信息原封不动发回的方法来实现Echo功能,代码如下: CNewSocket::OnReceive(int nErrorCOde) { m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); // 直接转发消息 AsyncSelect(FD_WRITE); } CNewSocket::OnSend(int nErrorCode) { Send(m_szBuffer,m_nLength,0); } 2. 建立一个CMyServerSocket类,重载CAsyncSocket类的OnAccept函数代码如下 在MyServerSocket.h中声明变量 public:: CNewSocket* m_pSocket; void CMyServerSocket::OnAccept(int nErrorCode) { //侦听到连接请求,调用Accept函数 CNewSocket* pSocket = new CNewSocket(); if (Accept(*pSocket)) { pSocket- >AsyncSelect(FD_READ); m_pSocket=pSocket; } else delete pSocket; } 3. 为对话框添加一个“侦听”按钮,添加如下代码: 在CsockServerDlg.ccp中声明变量 public: CMyServerSocket m_srvrSocket; void CCSockServerDlg::OnListen() { if (m_srvrSocket.m_hSocket==INVALID_SOCKET) { BOOL bFlag=m_srvrSocket.Create (UserPort,SOCK_STREAM,FD_ACCEPT); if (!bFlag) { AfxMessageBox(“Socket Error!”); M_srvrSocket.Close(); PostQuitMessage(0); Return; } } //“侦听”成功,等待连接请求 if (!m_srvrSocket。Listen(1)) { int nErrorCode = m_srvrSocket.GetLastError(); if (nError!=WSAEWOULDBLOCK) { AfxMessageBox(“Socket Error!”); M_srvrSocket.Close(); TCP/IP网络通信程序设计 本文介绍了TCP/IP网络应用程序的面向对象设计方法,并给出了用Visual C++4.2中MFC在Windows 95环境下开发的程序实例。 1 Sockets与Winsock 95 Winsock 95是在Unix Sockets及Windows Sockets基础上发展起来的。Sockets原是BSD为了Unix支持互联网通信而设计的4.3BSD Unix版本中的API,它采用客户-服务器模式的通信机制,使网络客户方和服务器方通过Sockets实现网络之间的联接和数据交换;Win dows Sockets描述定义了一个Microsoft Windows的网络编程界面,它为Windows TCP/IP 提供了一个BSD型套接字,除与4.3BSD Unix Sockets完全兼容外,还包括一个扩充文件,通过一组附加的API实现Windows式(即事件驱动)的编程风格;而Winsock 95则是在Microso ft Windows 95中进行网络应用程序设计的接口。Windows 95在Internet支配域中的TCP /IP协议定义了Winsock 95网络编程规范,溶入了许多新特点。MFC中提供了相应的CSock et类来实现网络通信。 2 Sockets编程原理 Sockets同时支持数据流Sockets和数据报Sockets。 下面是利用Socket进行通信连接的过程框图。其中图1是面向连接的时序图,图2是无连接的时序图。图1图2 由图可以看出,客户与服务器的关系是不对称的。对于TCP C/S,服务器首先启动,然后在某一时刻启动客户与服务器建立连接。服务器与客户开始都必须调用socket()建立一个套接字socket,然后服务器调用bind()将套接字与一个本地网络地址捆扎在一起,再调用listen()使套接字处于一种被动的准备接收状态,同时规定它的请求队列长度,之后服务器就可以调用accept()来接收客户连接。客户打开套接字之后,便可通过调用conne ct()和服务器建立连接。连接建立之后,客户和服务器之间就可以通过连接发送和接收数据。最后,待数据传送结束,双方调用closesocket()关闭套接字。对于UDP C/S,客户并不与服务器建立一个连接,而仅仅给服务器发送一张包含服务器地址的数据报。相似地,服务器也不从客户端接收一个连接,只是调用函数recvfrom,等待从客户端来的数据。依照recvfrom返回的协议地址以及数据报,服务器就可以给客户送一个应答。 3 Winsock 95编程方法 用Visual C++4.2以MFC在Windows 95中实现网络编程,主要就是利用CSocket类及其如下相关成员函数: (1)BOOL Create (UINT nSocketPort=0,int nSocketType=SOCK_STREAM,long lEvent=FD_READ|FD_WRITE|FD_OOD|FD_ACCEPT|FD_CONNECT|FD_CLOSE|,LPCTSTR|lpszSocket Address=NULL 该函数用来建立Socket。 (2)BOOL Bind(UINT nSocketPort,LPCTSTR lpszSocketAddess=NULL) 该函数的作用是将Socket端口与网络地址连接起来。 (3)BOOL Listen(int nConnectionBacklog=5) 该函数的作用是等待Socket请求。 (4)Virtual BOOL Accept(CAsyncSocket & rConnected Socket,Socket,SOCKADDR* lpSock Addr=NULL,int * lpSock AddrLen=NULL) 该函数的作用是取得队列上第一个连接请求并建立一个具有与Socket相同特性的套接字。 (5)BOOL Connect (LPCTSTR lpszHostAddress,UINT nHostPort) 该函数的作用是提出请求。其中,lpszHostAddress和nHostPort为接受请求进程的网络地址和Socket端口号。 (6)virtual void Close()该函数的作用是关闭Socket。 使用以上类及成员函数,按照以下步骤,就可以设计出合适的通信程序: Server:Construct→Creat→Bind→Listen→Accept→Send→Close; Client:Constuct→Creat→Connect→Receive→Close。 4 程序实例 我们用Visual C++4.2中MFC在Windows 95环境下设计了一个daytime cliont程序,清单如下: 头文件HEAD.H内容: #define IDM_STRAT 200 #define IDM_EDIT 200 class Mainwnd:public CFrame Wnd {public:Mainwnd(); afx_msg int OnCreat(LPCREATESTRUCT); afx_msg void OnStart(void); DECLARE_MESSAGE_MAP(); private:Cstatic CSStatic; CEdit LineEdit; CButten StartButton;}; class PengApp:public CWinApp {public:BOOL InitInstance();} 源程序Client.CPP清单: #include<afxwin.h> #include<winsock.h> #include "head.h" const int nPort=13; PengApp theApp; Main Wnd:Main Wnd() {if(!Create (NULL,"Communication Program",WS_OVERLAPPEDWINDOW,rectDefaul t)) AfxAbort();} int Mainwnd:OnCreate(LPCREATESTRUCT) {Rect rect;SetRect(& rect,80,50,160,70); Create("Host Name:",WS_CHILD|WS_VISIBLE|SS_LEFT,rect,this); SetRect(& rect,60,80,180,100); LineEdit.Create(WS_CHILD|WS_VISIBLE|WS_DLGFRAME|ES_LEFT,rect,this,IDM_ED IT); SetRect(&rect,100,120,140,140); StartButton,Create("start",WS_CHILD|VS_VISIBLE|BS_PUSHBUTTON,rect,this,I DM_START); return 0;} BEGIN_MESSAGE_MAP(Main Wnd,CFrameWnd) ON_WM_CREATE() ON_BN_CLICKED(IDM_START,OnStart) END_MESSAGE_MAP() BOOL ControlApp:InitInstance() {m_pMainWnd=new Main Wnd(); m_pMainWnd→ShowWindow (m_nCmdShow); m_pMainWnd→UpdateWindow(); return;} Void Main Wnd:Onstart(void) {CSocket TimeClient; if(! AfxSocketInit()) MessageBox("WindowsSocket initial failed!","Receiv e",MB_ICONSTOP); if(! TimeClient.Create()) MessageBox("ReceiveSocket create failed","Rece ive",MB_I(ON)STOP); else TimeClient.connect(strAddr,nPort); TimeClient.ReceiveFrom(csReceiveText,csCounts,LineEdit.GetWinText,nPort) MessageBox(TimeClient.csReceiveText); TimeClient.Close();} Clist的RemoveAll()函数速度很慢 vc6 和 builder6集成开发多文档的问题(D/JF有分,解决问题百分) 如何将一个小位图重复多次绘制到一个大的dc中 一个mfc内的矛盾? 请教高手,怎么才能将这样的操作速度提升? 如何实现可以任意加入任何控件的控件容器? 3000分求费尔防火墙源码及文档(2.0) 串口! 程序崩溃的问题 模板的小问题 How to use ExtCreateRegion 請教一個文本處理的問題
设为首页
广告刊例
经销商名单
技术讨论区
请输入关键词
首页 新闻聚焦 经典文章 疑难诊所 春秋茶楼
你目前所在的位置:程序春秋>>经典文章>>封面专题
如何使用MFC实现网络编程
--------------------------------------------------------------------------------
作者:传老鹰
随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于C++语言对底层操作的优越性,许多文章都曾经介绍过用VC++进行Socket编程的方法。但由于都是直接利用动态连接库wsock32.dll进
行操作,实现比较繁琐。其实,VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用他来实现Socket编程,是非常方便的。
本文将用一个Echo例程来介绍CAsyncSocket类的用法。
一. 客户端
1. 创建一个Dialog Based项目:CSockClient。
2. 设计对话框
去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中为CCSockClientDlg类添加变量。
Control ID Type Member
IDC_EDITMSG CEdit m_MSG
IDC_LISTMSG ClistBox m_MSGS
3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。
由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:以Public方式继承CAsyncSocket类,生成新类MySock;为MySock类添加虚函数OnReceive、OnConnect、OnSend。
4. 在MySock.ccp中添加以下代码
#include "CSockClient.h"
#include "CSockClientDlg.h"
5. 在MySock.h中添加以下代码
public:
BOOL m_bConnected; //是否连接
UINT m_nLength; //消息长度
char m_szBuffer[4096]; //消息缓冲区
6. 在MySock.ccp中重载各函数
MySock::MySock()
{
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
m_bConnected=FALSE;
}
MySock::~MySock()
{
//关闭套接字
if(m_hSocket!=INVALID_SOCKET)
Close();
}
void MySock::OnReceive(int nErrorCode)
{
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
//下面两行代码用来获取对话框指针
CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
memset(m_szBuffer,0,sizeof(m_szBuffer));
CAsyncSocket::OnReceive(nErrorCode);
}
void MySock::OnSend(int nErrorCode)
{
Send(m_szBuffer,m_nLength,0);
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
//继续提请一个“读”的网络事件,接收Server消息
AsyncSelect(FD_READ);
CAsyncSocket::OnSend(nErrorCode);
}
void MySock::OnConnect(int nErrorCode)
{
if (nErrorCode==0)
{
m_bConnected=TRUE;
CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
memcpy(m_szBuffer,"Connected to ",13);
strncat(m_szBuffer,pDlg- >m_szServerAdr,
sizeof(pDlg- >m_szServerAdr));
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
AsyncSelect(FD_READ); ////提请一个“读”的网络事件,准备接收
}
CAsyncSocket::OnConnect(nErrorCode);
}
7. 新建对话框IDD_Addr,用来输入IP地址和Port;生成新类CAddrDlg。增加两个Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中为CAddrDlg类添加变量。
Control ID Type Member
IDC_Addr CString m_Addr
IDC_Port Int m_Port
8. 在CSockClientDlg.ccp中添加代码:
#include "AddrDlg.h"
protected:
int TryCount;
MySock m_clientSocket;
UINT m_szPort;
public:
char m_szServerAdr[256];
9. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码:
void CCSockClientDlg::OnConnect()
{
m_clientSocket.ShutDown(2);
m_clientSocket.m_hSocket=INVALID_SOCKET;
m_clientSocket.m_bConnected=FALSE;
CAddrDlg m_Dlg;
//默认端口1088
m_Dlg.m_Port=1088;
if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty())
{
memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr));
m_szPort=m_Dlg.m_Port;
//建立计时器,每1秒尝试连接一次,直到连上或TryCount>10
SetTimer(1,1000,NULL);
TryCount=0;
}
}
10. 添加Windows消息WM_TIMER响应函数OnTimer
void CCSockClientDlg::OnTimer(UINT nIDEvent)
{
if (m_clientSocket.m_hSocket==INVALID_SOCKET)
{
BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT);
if(!bFlag)
{
AfxMessageBox("Socket Error!");
m_clientSocket.Close();
PostQuitMessage(0);
return;
}
}
m_clientSocket.Connect(m_szServerAdr,m_szPort);
TryCount++;
if (TryCount >=10 || m_clientSocket.m_bConnected)
{
KillTimer(1);
if (TryCount >=10)
AfxMessageBox("Connect Failed!");
return;
}
CDialog::OnTimer(nIDEvent);
}
11. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“发送”按钮,添加以下代码:
void CCSockClientDlg::OnSend()
{
if (m_clientSocket.m_bConnected)
{
m_clientSocket.m_nLength=m_MSG.GetWindowText
(m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer));
m_clientSocket.AsyncSelect(FD_WRITE);
m_MSG.SetWindowText("");
}
}
12. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码:
void CCSockClientDlg::OnExit()
{
//关闭Socket
m_clientSocket.ShutDown(2);
//关闭对话框
EndDialog(0);
}
12.运行此项目,连接时输入主机名或IP均可,CAsyncSocket类会自动处理。
二. 服务端
Server端的编程与Client端的类似,下面主要介绍他的Listen及Accept函数。
1. 建立一个CNewSocket类,重载CAsyncSocket类的OnReceive、OnSend函数,如何进行信息的显示和发送可以参考Client程序。本例中采用将收到信息原封不动发回的方法来实现Echo功能,代码如下:
CNewSocket::OnReceive(int nErrorCOde)
{
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
// 直接转发消息
AsyncSelect(FD_WRITE);
}
CNewSocket::OnSend(int nErrorCode)
{
Send(m_szBuffer,m_nLength,0);
}
2. 建立一个CMyServerSocket类,重载CAsyncSocket类的OnAccept函数代码如下
在MyServerSocket.h中声明变量
public::
CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int nErrorCode)
{
//侦听到连接请求,调用Accept函数
CNewSocket* pSocket = new CNewSocket();
if (Accept(*pSocket))
{
pSocket- >AsyncSelect(FD_READ);
m_pSocket=pSocket;
}
else
delete pSocket;
}
3. 为对话框添加一个“侦听”按钮,添加如下代码:
在CsockServerDlg.ccp中声明变量
public:
CMyServerSocket m_srvrSocket;
void CCSockServerDlg::OnListen()
{
if (m_srvrSocket.m_hSocket==INVALID_SOCKET)
{
BOOL bFlag=m_srvrSocket.Create
(UserPort,SOCK_STREAM,FD_ACCEPT);
if (!bFlag)
{
AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
PostQuitMessage(0);
Return;
}
}
//“侦听”成功,等待连接请求
if (!m_srvrSocket。Listen(1))
{
int nErrorCode = m_srvrSocket.GetLastError();
if (nError!=WSAEWOULDBLOCK)
{
AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
设为首页
广告刊例
经销商名单
技术讨论区
请输入关键词
首页 新闻聚焦 经典文章 疑难诊所 春秋茶楼
你目前所在的位置:程序春秋>>经典文章>>封面专题
如何使用MFC实现网络编程
--------------------------------------------------------------------------------
作者:传老鹰
随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于C++语言对底层操作的优越性,许多文章都曾经介绍过用VC++进行Socket编程的方法。但由于都是直接利用动态连接库wsock32.dll进
行操作,实现比较繁琐。其实,VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用他来实现Socket编程,是非常方便的。
本文将用一个Echo例程来介绍CAsyncSocket类的用法。
一. 客户端
1. 创建一个Dialog Based项目:CSockClient。
2. 设计对话框
去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,并按下表在ClassWizard中为CCSockClientDlg类添加变量。
Control ID Type Member
IDC_EDITMSG CEdit m_MSG
IDC_LISTMSG ClistBox m_MSGS
3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。
由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:以Public方式继承CAsyncSocket类,生成新类MySock;为MySock类添加虚函数OnReceive、OnConnect、OnSend。
4. 在MySock.ccp中添加以下代码
#include "CSockClient.h"
#include "CSockClientDlg.h"
5. 在MySock.h中添加以下代码
public:
BOOL m_bConnected; //是否连接
UINT m_nLength; //消息长度
char m_szBuffer[4096]; //消息缓冲区
6. 在MySock.ccp中重载各函数
MySock::MySock()
{
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
m_bConnected=FALSE;
}
MySock::~MySock()
{
//关闭套接字
if(m_hSocket!=INVALID_SOCKET)
Close();
}
void MySock::OnReceive(int nErrorCode)
{
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
//下面两行代码用来获取对话框指针
CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
memset(m_szBuffer,0,sizeof(m_szBuffer));
CAsyncSocket::OnReceive(nErrorCode);
}
void MySock::OnSend(int nErrorCode)
{
Send(m_szBuffer,m_nLength,0);
m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
//继续提请一个“读”的网络事件,接收Server消息
AsyncSelect(FD_READ);
CAsyncSocket::OnSend(nErrorCode);
}
void MySock::OnConnect(int nErrorCode)
{
if (nErrorCode==0)
{
m_bConnected=TRUE;
CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
memcpy(m_szBuffer,"Connected to ",13);
strncat(m_szBuffer,pDlg- >m_szServerAdr,
sizeof(pDlg- >m_szServerAdr));
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
AsyncSelect(FD_READ); ////提请一个“读”的网络事件,准备接收
}
CAsyncSocket::OnConnect(nErrorCode);
}
7. 新建对话框IDD_Addr,用来输入IP地址和Port;生成新类CAddrDlg。增加两个Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中为CAddrDlg类添加变量。
Control ID Type Member
IDC_Addr CString m_Addr
IDC_Port Int m_Port
8. 在CSockClientDlg.ccp中添加代码:
#include "AddrDlg.h"
protected:
int TryCount;
MySock m_clientSocket;
UINT m_szPort;
public:
char m_szServerAdr[256];
9. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码:
void CCSockClientDlg::OnConnect()
{
m_clientSocket.ShutDown(2);
m_clientSocket.m_hSocket=INVALID_SOCKET;
m_clientSocket.m_bConnected=FALSE;
CAddrDlg m_Dlg;
//默认端口1088
m_Dlg.m_Port=1088;
if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty())
{
memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr));
m_szPort=m_Dlg.m_Port;
//建立计时器,每1秒尝试连接一次,直到连上或TryCount>10
SetTimer(1,1000,NULL);
TryCount=0;
}
}
10. 添加Windows消息WM_TIMER响应函数OnTimer
void CCSockClientDlg::OnTimer(UINT nIDEvent)
{
if (m_clientSocket.m_hSocket==INVALID_SOCKET)
{
BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT);
if(!bFlag)
{
AfxMessageBox("Socket Error!");
m_clientSocket.Close();
PostQuitMessage(0);
return;
}
}
m_clientSocket.Connect(m_szServerAdr,m_szPort);
TryCount++;
if (TryCount >=10 || m_clientSocket.m_bConnected)
{
KillTimer(1);
if (TryCount >=10)
AfxMessageBox("Connect Failed!");
return;
}
CDialog::OnTimer(nIDEvent);
}
11. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“发送”按钮,添加以下代码:
void CCSockClientDlg::OnSend()
{
if (m_clientSocket.m_bConnected)
{
m_clientSocket.m_nLength=m_MSG.GetWindowText
(m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer));
m_clientSocket.AsyncSelect(FD_WRITE);
m_MSG.SetWindowText("");
}
}
12. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“关闭”按钮,添加以下代码:
void CCSockClientDlg::OnExit()
{
//关闭Socket
m_clientSocket.ShutDown(2);
//关闭对话框
EndDialog(0);
}
12.运行此项目,连接时输入主机名或IP均可,CAsyncSocket类会自动处理。
二. 服务端
Server端的编程与Client端的类似,下面主要介绍他的Listen及Accept函数。
1. 建立一个CNewSocket类,重载CAsyncSocket类的OnReceive、OnSend函数,如何进行信息的显示和发送可以参考Client程序。本例中采用将收到信息原封不动发回的方法来实现Echo功能,代码如下:
CNewSocket::OnReceive(int nErrorCOde)
{
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
// 直接转发消息
AsyncSelect(FD_WRITE);
}
CNewSocket::OnSend(int nErrorCode)
{
Send(m_szBuffer,m_nLength,0);
}
2. 建立一个CMyServerSocket类,重载CAsyncSocket类的OnAccept函数代码如下
在MyServerSocket.h中声明变量
public::
CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int nErrorCode)
{
//侦听到连接请求,调用Accept函数
CNewSocket* pSocket = new CNewSocket();
if (Accept(*pSocket))
{
pSocket- >AsyncSelect(FD_READ);
m_pSocket=pSocket;
}
else
delete pSocket;
}
3. 为对话框添加一个“侦听”按钮,添加如下代码:
在CsockServerDlg.ccp中声明变量
public:
CMyServerSocket m_srvrSocket;
void CCSockServerDlg::OnListen()
{
if (m_srvrSocket.m_hSocket==INVALID_SOCKET)
{
BOOL bFlag=m_srvrSocket.Create
(UserPort,SOCK_STREAM,FD_ACCEPT);
if (!bFlag)
{
AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
PostQuitMessage(0);
Return;
}
}
//“侦听”成功,等待连接请求
if (!m_srvrSocket。Listen(1))
{
int nErrorCode = m_srvrSocket.GetLastError();
if (nError!=WSAEWOULDBLOCK)
{
AfxMessageBox(“Socket Error!”);
M_srvrSocket.Close();
本文介绍了TCP/IP网络应用程序的面向对象设计方法,并给出了用Visual C++4.2
中MFC在Windows 95环境下开发的程序实例。
1 Sockets与Winsock 95
Winsock 95是在Unix Sockets及Windows Sockets基础上发展起来的。Sockets原
是BSD为了Unix支持互联网通信而设计的4.3BSD Unix版本中的API,它采用客户-服务器
模式的通信机制,使网络客户方和服务器方通过Sockets实现网络之间的联接和数据交
换;Win dows Sockets描述定义了一个Microsoft Windows的网络编程界面,它为
Windows TCP/IP 提供了一个BSD型套接字,除与4.3BSD Unix Sockets完全兼容外,还
包括一个扩充文件,通过一组附加的API实现Windows式(即事件驱动)的编程风格;而
Winsock 95则是在Microso ft Windows 95中进行网络应用程序设计的接口。Windows 95
在Internet支配域中的TCP /IP协议定义了Winsock 95网络编程规范,溶入了许多新特点。
MFC中提供了相应的CSock et类来实现网络通信。
2 Sockets编程原理
Sockets同时支持数据流Sockets和数据报Sockets。
下面是利用Socket进行通信连接的过程框图。其中图1是面向连接的时序图,图2是
无连接的时序图。
图1
图2
由图可以看出,客户与服务器的关系是不对称的。对于TCP C/S,服务器首先启动,
然后在某一时刻启动客户与服务器建立连接。服务器与客户开始都必须调用socket()
建立一个套接字socket,然后服务器调用bind()将套接字与一个本地网络地址捆扎在一
起,再调用listen()使套接字处于一种被动的准备接收状态,同时规定它的请求队列长
度,之后服务器就可以调用accept()来接收客户连接。客户打开套接字之后,便可通过
调用conne ct()和服务器建立连接。连接建立之后,客户和服务器之间就可以通过连接
发送和接收数据。最后,待数据传送结束,双方调用closesocket()关闭套接字。对于
UDP C/S,客户并不与服务器建立一个连接,而仅仅给服务器发送一张包含服务器地址
的数据报。相似地,服务器也不从客户端接收一个连接,只是调用函数recvfrom,等待
从客户端来的数据。依照recvfrom返回的协议地址以及数据报,服务器就可以给客户
送一个应答。
3 Winsock 95编程方法
用Visual C++4.2以MFC在Windows 95中实现网络编程,主要就是利用CSocket类及
其如下相关成员函数:
(1)BOOL Create (UINT nSocketPort=0,int nSocketType=SOCK_STREAM,long lEvent=FD_READ|FD_WRITE|FD_OOD|FD_ACCEPT|FD_CONNECT|FD_CLOSE|,LPCTSTR|lpszSocket Address=NULL
该函数用来建立Socket。
(2)BOOL Bind(UINT nSocketPort,LPCTSTR lpszSocketAddess=NULL)
该函数的作用是将Socket端口与网络地址连接起来。
(3)BOOL Listen(int nConnectionBacklog=5)
该函数的作用是等待Socket请求。
(4)Virtual BOOL Accept(CAsyncSocket & rConnected Socket,Socket,SOCKADDR* lpSock Addr=NULL,int * lpSock AddrLen=NULL)
该函数的作用是取得队列上第一个连接请求并建立一个具有与Socket相同特性的套接字。
(5)BOOL Connect (LPCTSTR lpszHostAddress,UINT nHostPort)
该函数的作用是提出请求。其中,lpszHostAddress和nHostPort为接受请求进
程的网络地址和Socket端口号。
(6)virtual void Close()该函数的作用是关闭Socket。
使用以上类及成员函数,按照以下步骤,就可以设计出合适的通信程序:
Server:Construct→Creat→Bind→Listen→Accept→Send→Close;
Client:Constuct→Creat→Connect→Receive→Close。
4 程序实例
我们用Visual C++4.2中MFC在Windows 95环境下设计了一个daytime cliont程序,
清单如下:
头文件HEAD.H内容:
#define IDM_STRAT 200
#define IDM_EDIT 200
class Mainwnd:public CFrame Wnd
{public:Mainwnd();
afx_msg int OnCreat(LPCREATESTRUCT);
afx_msg void OnStart(void);
DECLARE_MESSAGE_MAP();
private:Cstatic CSStatic;
CEdit LineEdit;
CButten StartButton;};
class PengApp:public CWinApp
{public:BOOL InitInstance();}
源程序Client.CPP清单:
#include<afxwin.h>
#include<winsock.h>
#include "head.h"
const int nPort=13;
PengApp theApp;
Main Wnd:Main Wnd()
{if(!Create (NULL,"Communication Program",WS_OVERLAPPEDW
INDOW,rectDefaul t)) AfxAbort();}
int Mainwnd:OnCreate(LPCREATESTRUCT)
{Rect rect;SetRect(& rect,80,50,160,70);
Create("Host Name:",WS_CHILD|WS_VISIBLE|SS_LEFT,rect,thi
s);
SetRect(& rect,60,80,180,100);
LineEdit.Create(WS_CHILD|WS_VISIBLE|WS_DLGFRAME|ES_LEFT,
rect,this,IDM_ED IT);
SetRect(&rect,100,120,140,140);
StartButton,Create("start",WS_CHILD|VS_VISIBLE|BS_PUSHBU
TTON,rect,this,I DM_START);
return 0;}
BEGIN_MESSAGE_MAP(Main Wnd,CFrameWnd)
ON_WM_CREATE()
ON_BN_CLICKED(IDM_START,OnStart)
END_MESSAGE_MAP()
BOOL ControlApp:InitInstance()
{m_pMainWnd=new Main Wnd();
m_pMainWnd→ShowWindow (m_nCmdShow);
m_pMainWnd→UpdateWindow();
return;}
Void Main Wnd:Onstart(void)
{CSocket TimeClient;
if(! AfxSocketInit()) MessageBox("WindowsSocket initial
failed!","Receiv e",MB_ICONSTOP);
if(! TimeClient.Create()) MessageBox("ReceiveSocket crea
te failed","Rece ive",MB_I(ON)STOP);
else TimeClient.connect(strAddr,nPort);
TimeClient.ReceiveFrom(csReceiveText,csCounts,LineEdit.G
etWinText,nPort)
MessageBox(TimeClient.csReceiveText);
TimeClient.Close();}