WSAAsyncSelect模型允许应用程序以Windows消息的方式接收网络事件通知。许多对性能要求不高的网络应用程序都采用WSAAsyncSelect模型,MFC的CSocket类也使用了它。
WSAAsyncSelect自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄,当有网络事件发生时,便向这个窗口发送消息。
intWSAAsyncSelect(
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; }
void CtestDlg::OnDestroy() { CDialog::OnDestroy(); WSACleanup(); // TODO: 在此处添加消息处理程序代码 }