600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 糖儿飞教你学C++ Socket网络编程——18. MFC WinSock版的TCP通信程序

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

时间:2022-10-13 15:41:45

相关推荐

糖儿飞教你学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;。这样才能在自定义函数中访问套接字。

提示:如果是VS,则在“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字符串型数据?

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。