异步选择(WSAAsyncSelect)模型是一个异步 I/O 模型。利用这个模型,应用程序可在一个套接字上,接收以 Windows 消息机制为基础的网络事件通知,开发者将socket注册到消息机制,当socket有事件(新的连接,新的数据,连接断开,可以写入)来时候。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。
该模型的核心即是WSAAsyncSelect函数,该函数是非阻塞的。
相同点:
他们都可以对Windows套接字应用程序所使用的多个套接字进行有效的管理。
不同点:
1.WSAAsyncSelect模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续执行;
2.发生网络事件时,应用程序得到的通知方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。
3.WSAAsyncSelect模型应用在基于消息的Windos环境下,使用该模型时必须创建窗口。而Select模型广泛应用在Unix系统和Windows系统,使用该模型不需要创建窗口。
4.应用程序调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变套接字的工作方式
int WSAAsyncSelect(
__in SOCKET s, //指定的是我们感兴趣的那个套接字。
__in HWND hWnd, //指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。
__in unsigned int wMsg, //指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。
__in long lEvent //指定一个位掩码,对应于一系列网络事件的组合
);
WSAAsyncSelect模型是Select模型的异步版本,在调用select()函数时,会发生阻塞现象。可以通过select()函数timeout参数,设置函数调用的阻塞时间。在设定的时间内,线程保持等待,直到其中一个或多个套接字满足可读可写的条件时,该函数返回。
不适合高并发网络结构,因为是基于窗口消息机制,消息太多就处理速度较慢。
创建windwos窗口项目:
//1.添加头文件
#include<WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#define WM_MYSOCKMSG WM_USER+1
bool HandleData(SOCKET sockClient);//处理收发数据
//2.初始化WSAstartup
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return 0;
}
/* Confirm that the WinSock DLL supports 1.1.*/
/* Note that if the DLL supports versions greater */
/* than 1.1 in addition to 1.1, it will still return */
/* 1.1 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return 0;
}
//3.再处理消息处添加代码
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//1) 创建socket
SOCKET sockServer = socket(
AF_INET,
SOCK_STREAM, //流式
IPPROTO_TCP);//tcp协议
// 2) 绑定端口
sockaddr_in siServer;
siServer.sin_family = AF_INET;
siServer.sin_port = htons(9527);
siServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
if (nRet == SOCKET_ERROR)
{
printf("绑定失败 \r\n");
return 0;
}
// 3) 监听
nRet = listen(sockServer, SOMAXCONN);
if (nRet == SOCKET_ERROR)
{
printf("监听失败 \r\n");
return 0;
}
//4)接受连接
//这个socket只关系新连接和关闭时间
WSAAsyncSelect(sockServer, hWnd, WM_MYSOCKMSG, FD_ACCEPT | FD_CLOSE);
break;
}
case WM_MYSOCKMSG:
{
SOCKET sock = (SOCKET)wParam;
WORD wErrCode = WSAGETSELECTERROR(lParam);
WORD wSelectEvent = WSAGETSELECTEVENT(lParam);
switch (wSelectEvent)
{
case FD_ACCEPT:
{
sockaddr_in siClient;
int nSize = sizeof(siClient);
SOCKET sockClient = accept(sock, (sockaddr*)&siClient, &nSize);
//为新的连接注册对应的网络事件
WSAAsyncSelect(sockClient, hWnd, WM_MYSOCKMSG, FD_READ | FD_CLOSE);
break;
}
case FD_READ://数据来了
{
HandleData(sock); //处理数据
break;
}
case FD_CLOSE://连接断开
{
//将sock从网络事件中移除
closesocket(sock);
break;
}
}
break;
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
bool HandleData(SOCKET sockClient)
{
// 5) 收发数据
char aryBuff[MAXWORD] = { 0 };
int nRet = recv(sockClient, aryBuff, sizeof(aryBuff), 0);
if (nRet == 0 || nRet == SOCKET_ERROR)
{
printf("接受数据失败 \r\n");
return false;
}
printf("收到数据: %s \r\n", aryBuff);
char szBuff[] = { "recv OK " };
nRet = send(sockClient, szBuff, sizeof(szBuff), 0);
if (nRet == SOCKET_ERROR)
{
printf("数据发送失败 \r\n");
return false;
}
return true;
}
原文:https://www.cnblogs.com/zhaoyixiang/p/12964762.html