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);
- 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: 在此处添加消息处理程序代码
- }