WSAAsyncSelect( )是Winsock提供的一个适合于Windows编程使用的函数,它允许在一个套接口上当发生特定的网络事件时,给Windows网络应用程序(窗口或对话框)发送一个消息(事件通知)。
WSAAsyncSelect函数原型如下:
int WSAAsyncSelect(
SOCKET s, //标识一个需要事件通知的套接口描述符
HWND hWnd, //标识一个在网络事件发生时要想收到消息的窗口或对话框的句柄
u_int wMsg, //在网络事件发生时要接收的消息,该消息会投递到由hWnd句柄指定的窗口或对话框
long lEvent //位屏蔽码,用于指明应用程序感兴趣的网络事件集合
);
若应用程序对一个套接口s调用了WSAAsyncSelect( )函数,那么套接口s的模式会自动从阻塞模式变成非阻塞模式。如果应用程序同时对多个网络事件感兴趣,那么只需对各种类型的网络事件执行按位或(OR)的运算即可。进行一次WSAAsyncSelect( )调用,将使为同一个套接口启动的所有以前的WSAAsyncSelect( )调用作废。如果要取消所有的通知,也就是指出Windows Sockets的实现不再在套接口上发送任何和网络事件相关的消息,则把lEvent字段置为0,然后调用WSAAsyncSelect( )。当某一套接口s上发生了一个已命名的网络事件时,应用程序窗口hWnd会接收到消息wMsg。应用程序窗口例程的wParam参数标识了网络事件发生的套接口。lParam参数的低位字指明了发生的网络事件,高位字则含有一个错误代码,该错误代码可以是Winsock2.h中定义的任何错误。
以下为测试WSAAsyncSelect()函数的程序,一个服务器端两个客户端
下面是服务器端程序:
#define WM_SOCKET WM_USER+101
#include <WINSOCK2.H>
/*#include <windows.h>*/
#pragma comment(lib,"WS2_32")
//----------------窗口过程函数的声明-------------
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//----------------WinMain()函数------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
WNDCLASS wc;
wc.style=CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc=WindowProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hInstance;
wc.hIcon=LoadIcon(NULL,IDI_EXCLAMATION);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName=NULL;
wc.lpszClassName="Test";
//---注册窗口类----
RegisterClass(&wc);
//---创建窗口----
HWND hwnd=CreateWindow("Test","窗口标题",WS_SYSMENU,300,0,600,400,NULL,NULL,hInstance,NULL);
if (hwnd==NULL)
{
MessageBox(hwnd,"创建窗口出错","标题栏提升",MB_OK);
return 1;
}
//---显示窗口----
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
//---socket-----
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(2,2);
if (WSAStartup(wVersionRequested,&wsaData)!=0)
{
MessageBox(NULL,"WSAStartup() Failed","调用失败",0);
return 1;
}
SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (s==INVALID_SOCKET)
{
MessageBox(NULL,"socket() Failed","调用失败",0);
return 1;
}
sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(1111);
sin.sin_addr.S_un.S_addr=inet_addr("192.168.31.1");
if (bind(s,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
{
MessageBox(NULL,"bind() Failed","调用失败",0);
return 1;
}
if (listen(s,3)==SOCKET_ERROR)
{
MessageBox(NULL,"listen() Failed","调用失败",0);
return 1;
}
else
MessageBox(hwnd,"进入监听状态!","标题栏提示",MB_OK);
WSAAsyncSelect(s,hwnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);
//---消息循环----
MSG msg;
while (GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
closesocket(s);
WSACleanup();
return msg.wParam;
}
//-------------------窗口过程----------------------
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch(uMsg)
{
case WM_SOCKET:
{
SOCKET ss=wParam; //wParam参数标志了网络事件发生的套接口
if (WSAGETSELECTERROR(lParam))
{
closesocket(ss);
return 0;
}
switch (WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT: //-----①连接请求到来
{
sockaddr_in Cadd;
int Cadd_len=sizeof(Cadd);
SOCKET sNew=accept(ss,(sockaddr*)&Cadd,&Cadd_len);
if (ss==INVALID_SOCKET)
MessageBox(hwnd,"调用accept()失败!","标题栏提示",MB_OK);
WSAAsyncSelect(sNew,hwnd,WM_SOCKET,FD_READ|FD_CLOSE);
}break;
case FD_READ: //-----②数据发送来
{
char cbuf[256];
memset(cbuf,0,256);
int cRecv=recv(ss,cbuf,256,0);
if ((cRecv==SOCKET_ERROR&& WSAGetLastError() == WSAECONNRESET)|| cRecv==0)
{
MessageBox(hwnd,"调用recv()失败!","标题栏提示",MB_OK);
closesocket(ss);
}
else if (cRecv>0)
{
MessageBox(hwnd,cbuf,"收到的信息",MB_OK);
char Sbuf[]="Hello client!I am server";
int isend=send(ss,Sbuf,sizeof(Sbuf),0);
if (isend==SOCKET_ERROR || isend<=0)
{
MessageBox(hwnd,"发送消息失败!","标题栏提示",MB_OK);
}
else
MessageBox(hwnd,"已经发信息到客户端!","标题栏提示",MB_OK);
}
}break;
case FD_CLOSE: //----③关闭连接
{
closesocket(ss);
}
break;
}
}
break;
case WM_CLOSE:
if (IDYES==MessageBox(hwnd,"是否确定退出?","message",MB_YESNO))
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
客户端与上两次的都一样,这里不再给出。详见这里 juejin.cn/post/698095… 先启动服务器端,再启动1号客户端,接着启动2号客户端,就会看到如图所示: