糖儿飞教你学C++ Socket网络编程——18. MFC WinSock版的TCP通信程序

在4.2节中使用Win32 API方法制作了一个TCP异步通信的程序,本节将4.2节的程序用MFC框架重新编写,改写后程序的界面如图6-15所示,功能与4.2节的程序完全相同。

 

图6-15 MFC版TCP异步通信程序的界面

6.3.1 服务器端程序的制作

1)创建一个MFC工程:新建工程,选择“MFC APPWizard(exe)”,输入工程名(如TCPzdy),单击“下一步”,在步骤1选择“基本对话框”,单击“完成”按钮。

2)在ResourceView选项卡,找到Dialog下的“IDD_TCPZDY_DIALOG”,将对话框的界面改为如图6-16所示,并设置各个控件的ID值。

图6-16 服务器端程序的界面

3)按“Ctrl+W”键,或者在对话框界面上按右键,选择“建立类向导”,打开“MFC类向导”对话框,在“Member Variables”选项卡中为控件设置成员变量如图6-17所示。

图6-17 设置成员变量

4)在*dlg.h文件中,添加如下引用头文件和声明自定义消息的代码。

#include "winsock2.h"

#define     WM_SOCKET  WM_USER+1           //自定义消息

#pragma comment(lib,"ws2_32.lib")

5)在*dlg.h文件中,声明套接字和地址变量。

class CTCPzdyDlg : public CDialog{

public:

        CTCPzdyDlg(CWnd* pParent = NULL);      // standard constructor

        SOCKET sockSer, sockConn;                    //声明两个套接字变量

        SOCKADDR_IN addrSer, addrCli;

……}

6)在*dlg.cpp文件中,为对话框的OnInitDialog()函数加入对话框初始化代码:

BOOL CTCPzdyDlg::OnInitDialog(){

       m_ip="127.0.0.1";

       m_port="5566";

       UpdateData(FALSE);

       c_send.EnableWindow(FALSE);

       return TRUE;        }

7)双击“创建服务器”按钮,为“创建服务器”按钮编写如下代码:

void CTCPzdyDlg::OnCreate() {

       WSADATA wsaData; 

    WORD sockVersion=MAKEWORD(2,2); 

    if(WSAStartup(sockVersion,&wsaData)) {      

        AfxMessageBox(_T("初始winsock函数库失败!"),MB_OK|MB_ICONSTOP); 

    }    

       HWND hwnd = AfxGetMainWnd()->m_hWnd;   //获得窗口句柄    

       sockSer=socket(AF_INET,SOCK_STREAM,0);

       //设置异步方式

WSAAsyncSelect(sockSer, hwnd,WM_SOCKET,FD_ACCEPT |FD_READ |FD_CLOSE);

       UpdateData();

       addrSer.sin_family=AF_INET;

       addrSer.sin_port=htons(atoi(m_port));

       addrSer.sin_addr.S_un.S_addr=inet_addr(m_ip);

       int len =sizeof(SOCKADDR);

       bind(sockSer,(SOCKADDR*) &addrSer,len);     //绑定地址

       listen(sockSer,5);                 //监听

       c_send.EnableWindow(TRUE);

       c_create.EnableWindow(FALSE);                             

}

8)从本步骤到第10)步,将采用自定义消息的方式实现程序异步接收消息功能。在*dlg.h文件中,声明消息处理函数,在“//{{AFX_MSG(CTCPzdyDlg)”下添加下面一行:

       afx_msg LRESULT OnRecvData(WPARAM wParam,LPARAM lParam);

9)在*dlg.cpp文件中,创建消息映射,在BEGIN_MESSAGE_MAP(CTCPzdyDlg, CDialog)下面添加如下消息映射代码:

ON_MESSAGE(WM_SOCKET,OnRecvData)

10)在*dlg.cpp文件中,编写自定义消息的处理函数,代码如下:

CString clibuf="客户端: >"; CString serbuf="服务器: >";

int len =sizeof(SOCKADDR);

LRESULT CTCPzdyDlg::OnRecvData(WPARAM wParam,LPARAM lParam) { 

    SOCKET s=wParam;          //发生事件的套接字标识符由wParam通知

       char recvbuf[256];

    if(WSAGETSELECTERROR(lParam))  {  //如果选择事件出错

        closesocket(s); 

        return false;      } 

       switch (WSAGETSELECTEVENT(lParam)) {    //事件名由lParam通知

       case FD_ACCEPT: {                     //接收请求事件

              sockConn=accept(sockSer,(SOCKADDR*) &addrCli,&len);

              }

              break;

       case FD_READ: {                       //可读事件

              recv(sockConn,recvbuf,256,0);

              clibuf+=recvbuf;   

              c_recvbuf.AddString(clibuf);       

              clibuf="客户端: >";             //重新给字符串赋值

                              }

              break;

       case FD_CLOSE: {                     //关闭连接事件

              AfxMessageBox( "客户端已断开连接");            }

              break;

       default:

              break;     }           

    return true; 

11)双击“发送”按钮,为“发送”按钮编写发送消息的代码:

void CTCPzdyDlg::OnSend() {

       char buff[200];

       char * ct;

       CTime time = CTime::GetCurrentTime();            //获取当前时间

       CString t = time.Format("        %H:%M:%S");     //设置时间显示格式

       ct=(char*)t.GetBuffer(0);      //cstring 转 char*

       c_sendbuf.GetWindowText(buff,200);

       c_sendbuf.SetWindowText(NULL);

       CString Ser="服务器: >";

       strcat(buff,ct);

       send(sockConn,buff,strlen(buff)+1,0);

       c_recvbuf.AddString(Ser+buff);

}

总结:

本程序中使用了自定义消息WM_SOCKET,在MFC中自定义消息可分为4步:

第1步:自定义消息,例如:#define  WM_SOCKET  WM_USER+1

第2步:声明消息处理函数

afx_msg LRESULT OnRecvData(WPARAM wParam, LPARAM lParam)

(以上两步都写在*dlg.h头文件中)

第3步:创建消息映射

ON_MESSAGE(WM_SOCKET,OnRecvData)

写在:BEGIN_MESSAGE_MAP(CTCPzcliDlg, CDialog)一行的下面。

第4步 编写消息处理函数

LRESULT CTCPzdyDlg::OnRecvData(WPARAM wParam,LPARAM lParam) 

{            ……    }

(以上两步都写在*dlg.cpp文件中)

与4.2节的Win32 API版程序相比,本节的程序必须在OnRecvData()函数中,获取套接字标识符,方法是:SOCKET s=wParam;。这样才能在自定义函数中访问套接字。

提示:如果是VS2010,则在“MFC类向导”对话框的消息选项卡中,单击“添加自定义消息”,在弹出的对话框中输入自定义消息名和消息处理程序名,就可自动生成上述自定义消息的代码。

6.3.2 客户端程序的制作

1)创建一个MFC工程:新建工程,选择“MFC APPWizard(exe)”,输入工程名(如TCPzcli),单击“下一步”,在步骤1选择“基本对话框”,单击“完成”按钮。

2)在ResourceView选项卡,找到Dialog下的“IDD_TCPZCLI_DIALOG”,将对话框的界面改为如图6-18所示,并设置各个控件的ID值。

图6-18 客户端程序界面及控件ID

3)按“Ctrl+W”键,或者在对话框界面上按右键,选择“建立类向导”,打开“MFC类向导”对话框,在“Member Variables”选项卡中为控件设置成员变量如图6-19所示。

图6-19 设置成员变量

4)在*dlg.h文件中,添加如下引用头文件和声明自定义消息的代码。

#include "winsock2.h"

#define     WM_SOCKET  WM_USER+1           //自定义消息

#pragma comment(lib,"ws2_32.lib")

5)在*dlg.h文件的对话框类中,声明套接字变量和地址变量。代码如下

class CTCPzcliDlg : public CDialog{

public:

       CTCPzcliDlg(CWnd* pParent = NULL);      // standard constructor

       SOCKET sockCli;                     //客户端套接字    

       SOCKADDR_IN addrSer, addrCli;

……        }

6)在*dlg.cpp文件中,为对话框的OnInitDialog()函数加入对话框初始化代码:

BOOL CTCPzcliDlg::OnInitDialog(){

       m_ip="127.0.0.1";                       //!!!

       m_port="5566";

       UpdateData(FALSE);

       c_send.EnableWindow(FALSE);

       return TRUE;  // return TRUE  unless you set the focus to a control

}

7)双击“连接服务器”按钮,为“连接服务器”按钮编写如下代码:

void CTCPzcliDlg::OnConn() {

WSADATA wsaData; 

    WORD sockVersion=MAKEWORD(2,2); 

    if(WSAStartup(sockVersion,&wsaData))      { 

        AfxMessageBox(_T("初始winsock函数库失败!"),MB_OK|MB_ICONSTOP); 

       }     

       HWND hwnd = AfxGetMainWnd()->m_hWnd;  

       sockCli=socket(AF_INET,SOCK_STREAM,0);

       WSAAsyncSelect(sockCli, hwnd,WM_SOCKET,FD_CONNECT |FD_READ |  FD_CLOSE);      //设置异步方式

       UpdateData();

       addrSer.sin_family=AF_INET;

       addrSer.sin_port=htons(atoi(m_port));

       addrSer.sin_addr.S_un.S_addr=inet_addr(m_ip);

       int res=connect(sockCli,(SOCKADDR*)&addrSer,sizeof(SOCKADDR));

       if(res==0){     AfxMessageBox("客户端连接服务器失败");}

              else 

                            AfxMessageBox("客户端连接服务器成功");

       c_send.EnableWindow(TRUE);

       c_conn.EnableWindow(FALSE);         

}

8)在*dlg.h文件中,声明消息处理函数,在“//{{AFX_MSG(CTCPzcliDlg)”下添加下面一行:

afx_msg LRESULT OnRecvData(WPARAM wParam,LPARAM lParam);

9)在*dlg.cpp文件中,创建消息映射,在BEGIN_MESSAGE_MAP(CTCPzdyDlg, CDialog)下面添加下面一行:

ON_MESSAGE(WM_SOCKET,OnRecvData)

10)在*dlg.cpp文件中,编写自定义消息的处理函数,代码如下:

CString serbuf="服务器: >";

LRESULT CTCPzcliDlg::OnRecvData(WPARAM wParam,LPARAM lParam){

      SOCKET s=wParam; 

    CString strContent; 

       char recvbuf[256];

    if(WSAGETSELECTERROR(lParam))      { 

        closesocket(s); 

        return false;      } 

switch (WSAGETSELECTEVENT(lParam))       {

              case FD_CONNECT:   //接收请求事件

                     {                          }

                     break;

              case FD_READ: {   //可读事件               

              recv(sockCli,recvbuf,256,0);

              serbuf+=recvbuf;  

              c_recvbuf.AddString(serbuf);             

              serbuf="客户端: >";  //重新给字符串赋值

              }

                     break;

              case FD_CLOSE:  //关闭连接事件

                     {AfxMessageBox( "正常关闭连接");                 }

                     break;                          }

    return true;   

}

11)双击“发送”按钮,为“发送”按钮编写如下代码:

void CTCPzcliDlg::OnSend() {

       char buff[200];

       char * ct;

       CTime time = CTime::GetCurrentTime();            //获取当前时间

       CString t = time.Format("        %H:%M:%S");     //设置时间显示格式

       ct=(char*)t.GetBuffer(0);      //cstring 转 char*

       c_sendbuf.GetWindowText(buff,200);

       c_sendbuf.SetWindowText(NULL);

       CString Cli="客户端: >";

       strcat(buff,ct);

       send(sockCli,buff,strlen(buff)+1,0);    

       c_recvbuf.AddString(Cli+buff);          

}

12)当单击“退出”按钮时,关闭套接字,实现方法是:在“工程名.cpp”文件中,找到按钮IDOK的消息处理代码,修改如下:

       int nResponse = dlg.DoModal();

       if (nResponse == IDOK)       {

              closesocket(dlg.sockCli);             //这句是添加的代码,用来关闭套接字

       }

总结:将Win32API程序转换成MFC WinSock程序的步骤如下:

① 将Win32 API程序中引用的头文件都放到*dlg.h文件中;

② 创建的套接字变量和地址变量放到*dlg.h文件:class *Dlg{}类的public变量中;

③ WSAStartup() socket() bind() listen()放到”启动服务器”按钮或OnInitDialog() 函数中;

④ accept()函数放到FD_ACCEPT事件(异步)或OnInitDialog() 函数中(同步);

⑤ send()函数放到“发送”按钮中;

⑥ recv()函数放到“接收”按钮或FD_READ事件中。

习题

1. 在c_send.EnableWindow(TRUE)中,c_send是           变量(填控件或值)。

UpdateData(FALSE)的作用是                           ,UpdateData()的作用是                   

2. 在MFC中,要获取单个编辑框中的文本,可使用                         函数,要设置单个编辑框的文本,可使用                           

3. 对于m_result=itoa(result,temp,10);,itoa函数的功能是                    ,其中第3个参数10表示        

4. 在MFC中,要向列表框中添加内容,需要使用                             函数。

5. MFC中,要弹出一个打开文件对话框,需要用到                                  类。

6. 要弹出一个模态对话框,需要使用对话框类的对象的                    方法。

7. IDC_btn.EnableWindow(TRUE);表示                                                             

8. 在MFC中,怎样把字符数组转换为CString字符串型数据?

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值