WSAAsyncSelect模型是Windows Socket的一个异步I/O模型。
利用该模型可以接收以Windows消息为基础的socket网络事件。
Windows sockets应用程序在创建套接字后,调用WSAAsyncSelect函数注册感兴趣的网络事件,当该事件发生时Windows窗口收到消息,应用程序就可以对接收到的网络事件进行处理。
WSAAsyncSelect是select模型的异步版本。在应用程序使用select函数时会发生阻塞现象。可以通过select的timeout参数设置阻塞的时间。在设置的时间内,select函数等待,直到一个或多个Socket满足可读或可写的条件。
利用该模型可以接收以Windows消息为基础的socket网络事件。
Windows sockets应用程序在创建套接字后,调用WSAAsyncSelect函数注册感兴趣的网络事件,当该事件发生时Windows窗口收到消息,应用程序就可以对接收到的网络事件进行处理。
WSAAsyncSelect是select模型的异步版本。在应用程序使用select函数时会发生阻塞现象。可以通过select的timeout参数设置阻塞的时间。在设置的时间内,select函数等待,直到一个或多个Socket满足可读或可写的条件。
而WSAAsyncSelect是非阻塞的。Windows sockets程序在调用recv之前,调用WSAAsyncSelect注册网络事件。WSAAsyncSelect函数立即返回。当系统中数据准备好时,会向应用程序发送消息。此此消息的处理函数中可以调用recv接收数据。
WSAAsyncSelect模型与select模型的相同点是它们都可以对多个套接字进行管理。但它们也有不小的区别。
首先WSAAsyncSelect模型是异步的,且通知方式不同。
更重要的一点是:WSAAsyncSelect模型应用在基于消息的Windows环境下,使用该模型时必须创建窗口,而select模型可以广泛应用在Unix系统,使用该模型不需要创建窗口。
最后一点区别:应用程序在调用WSAAsyncSelect函数后,套接字就被设置为非阻塞状态。而使用select函数不改变套接字的工作方式。
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
#define WM_SOCKET (WM_USER + 1)
#define MAXDATASIZE 1024
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//回调函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("WSAAsyncSelect");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;//宽度高度改变时进行重绘
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))//注册类
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
//直接发送WM_CREATE消息
hwnd = CreateWindow(szAppName, // window class name
TEXT("WSAAsyncSelect"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
//到此,窗口在内存已经存在
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
//-----------Socket------------
WSADATA wsaData;
SOCKET sServer;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
return -1;
}
sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sServer == INVALID_SOCKET)
{
return -1;
}
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(9999);
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if (bind(sServer, (const struct sockaddr*)&addrServ, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
return -1;
}
if (listen(sServer, 5) == SOCKET_ERROR)
{
return -1;
}
WSAAsyncSelect(sServer, hwnd, WM_SOCKET, FD_ACCEPT | FD_CLOSE);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);//键盘消息转换
DispatchMessage(&msg);//把消息返回操作系统,系统callback
}
closesocket(sServer);
WSACleanup();
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) //回调函数,操作系统调用
{
HDC hdc;
PAINTSTRUCT ps;
static char buf[MAXDATASIZE], temp[MAXDATASIZE];
static char szDateTime[50];
SOCKET sClient;
SYSTEMTIME st;
static SOCKADDR_IN addrClient;
switch (message)
{
case WM_CREATE://可以进行动态链接库dll的载入
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOutA(hdc, 0, 0, buf, strlen(buf));
EndPaint(hwnd, &ps);
return 0;
case WM_SOCKET:
{
if (WSAGETSELECTERROR(lParam))
{
closesocket(wParam);
return -1;
}
switch (WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
if ((sClient = accept(wParam, (struct sockaddr *)&addrClient, NULL)) == INVALID_SOCKET)
{
closesocket(sClient);
break;
}
//MessageBoxA(NULL, inet_ntoa(addrClient.sin_addr), "IP", 0);
WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);
break;
case FD_READ:
GetLocalTime(&st);
memset(szDateTime, 0, sizeof(szDateTime));
sprintf(szDateTime, "%4d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
memset(temp, 0, sizeof(temp));
if (recv(wParam, temp, MAXDATASIZE, 0) == SOCKET_ERROR)
{
return -1;
}
//sprintf(buf, "%s, Recv From Client [%s:%d] :%s\n", szDateTime, inet_ntoa(addrClient.sin_addr), addrClient.sin_port, temp);
sprintf(buf, "%s, Recv From Client:%s\n", szDateTime, temp);
if (send(wParam, temp, strlen(temp), 0) == SOCKET_ERROR)
{
return -1;
}
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
break;
case FD_CLOSE:
closesocket(wParam);
break;
}
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);//默认系统消息处理
}