WSAAsyncSelect模型允许应用程序以Windows消息的方式接收网络事件通知。许多对性能要求不高的网络应用程序都采用WSAAsyncSelect模型,MFC的CSocket类也使用了它。
WSAAsyncSelect自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发生时,便向这个窗口发送消息。
int WSAAsyncSelect(
SOCKET s, //需要设置的套接字句柄
HWND hWnd, //指定一个窗口句柄, 套接字的通知消息将被发到此窗口中
u_int wMsg, //网络事件到来的ID,可以在WM_USER以上数值中任意指定一个值
long IEvent //指定哪些通知码需要发送
//FD_READ可以读套接字
//FD_WRITE 可以写套接字
//FD_ACCEPT 监听套接字有连接接入
//FD_CONNET 如果套接字连接对方主机,连接完成后会收到这个通知码
//FD_CLOSE 检测到套接字对应的连接被关闭
)
回传过来的消息类型与注册的wMsg相同,wParam等于套接字句柄,lParam通过WSAGETSELECTEVENT转义后就是FD_READ,FD_WRITE,FD_ACCEPT,FD_CONNET,FD_CLOSE
代码如下:
新建一个基于MFC对话框的工程,工程名为test,然后在testDlg.h中定义
#include "winsock2.h"
#pragma comment(lib, "WS2_32")
#define WM_SOCKET (WM_USER+1)
同时在该文件中加入一个自定义消息处理函数
afx_msg LRESULT OnSocketMsg(WPARAM wParam,LPARAM lParam);
在testDlg.cpp中定义
ON_MESSAGE(WM_SOCKET,OnSocketMsg)
在OnInitDialog中加入自己的初始化代码
// TODO: 在此添加额外的初始化代码
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
if(WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}
USHORT nPort=4567;
SOCKET s=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=ntohs(nPort);
sin.sin_addr.S_un.S_addr=INADDR_ANY;
if(bind(s,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
{
DWORD error=GetLastError();
return TRUE;
}
WSAAsyncSelect(s,m_hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);
listen(s,5);
添加自定义消息函数的处理:
LRESULT CtestDlg::OnSocketMsg(WPARAM wParam,LPARAM lParam)
{
SOCKET s=wParam;
CString strContent;
if(WSAGETSELECTERROR(lParam))
{
closesocket(s);
return false;
}
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT://检测到有套接字连上来
{
SOCKET client=accept(s,NULL,NULL);
WSAAsyncSelect(client,m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
CString strContent;
GetDlgItemText(IDC_EDIT1,strContent);
if(!strContent.IsEmpty())
{
strContent+="\r\n";
}
strContent+="有客户端连上来了";
SetDlgItemText(IDC_EDIT1,strContent);//将内容显示到Edit控件
}
break;
case FD_WRITE:
{
}
break;
case FD_READ:
{
char szText[1024]={0};
if(recv(s,szText,1024,0)==-1)
closesocket(s);
else
{
GetDlgItemText(IDC_EDIT1,strContent);
if(!strContent.IsEmpty())
{
strContent+="\r\n";
}
CString strTemp;
strTemp.Format("%s",szText);
strContent+=strTemp;
SetDlgItemText(IDC_EDIT1,strContent);//将内容显示到Edit控件中
}
}
break;
case FD_CLOSE:
{
closesocket(s);
GetDlgItemText(IDC_EDIT1,strContent);
if(!strContent.IsEmpty())
{
strContent+="\r\n";
}
strContent+="有客户端断开了";
SetDlgItemText(IDC_EDIT1,strContent);//将内容显示到Edit控件中
}
break;
}
return true;
}
对WM_DESTROY消息处理,清理套接字库
void CtestDlg::OnDestroy()
{
CDialog::OnDestroy();
WSACleanup();
// TODO: 在此处添加消息处理程序代码
}