WSAAsyncSelect模型
WSAAsyncSelect模型允许应用程序以Windows消息的接收网络事件通知。这个模型是为了适应Windows的消息驱动环境而设置的,现在许多对性能要求不高的网络应用程序都采用WSAAsyncSelect模型,MFC中的CSocket类也使用了它。
WSAAsyncSelect函数自动把套接字设为非阻塞模式,并且为套接字绑定一个窗口句柄和发送哪些通知码(FD_READ之类的)。当有网络事件发生时,便向这个窗口发送程序自定义的消息。
具体编程流程:
1、 自定义一个与网络通知消息,用于通知WSAsyncSelect绑定中的窗口。
2、 创建一个窗口,用于WSAAsyncSelect绑定到指定的套接字。
3、 编写上述窗口的消息处理函数。在收到自定义网络通知消息后,提取出相应错误码和网络事(FD_ACCEPT、 FD_READ、FD_WRIET、FD_CLOSE),并作出相应的处理。
#define _WIN32_WINNT 0x0400
#include<windows.h>
#include<cstdio>
#include"InitSocket.h"
//需要自定义一个消息
#define WM_SOCKET WM_USER+1
CInitSock initSock ; //进入main函数前已经进行了初始化
LONG CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) ;
int main(void)
{
char szClassName[] = "MainWClass" ;
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof(wndclass) ;
wndclass.style = CS_HREDRAW|CS_VREDRAW ;
wndclass.lpfnWndProc = WindowProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = NULL ;
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szClassName ;
wndclass.hIconSm = NULL ;
RegisterClassEx(&wndclass) ;
//创建主窗口
HWND hWnd = CreateWindowEx(
0,
szClassName,
"",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
NULL,
NULL
) ;
if(NULL == hWnd)
{
MessageBox(NULL,"创建窗口出错!","error",MB_OK) ;
return -1 ;
}
USHORT nPort = 4567 ;
SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) ;
sockaddr_in sin ;
sin.sin_family = AF_INET ;
sin.sin_port = htons(nPort) ;
sin.sin_addr.s_addr = INADDR_ANY ;
if(bind(sListen,(sockaddr*)&sin,sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind()\n") ;
return -1 ;
}
//将套接字设为窗口通知消息类型,自动将套接字设置为非阻塞模式
WSAAsyncSelect(sListen,hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE) ;
listen(sListen,5) ;
MSG msg ;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
return msg.wParam ;
}
LONG CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch(uMsg)
{
case WM_SOCKET :
{
SOCKET s = wParam ;
if(WSAGETSELECTERROR(lParam))
{
closesocket(s) ;
return 0 ;
}
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT :
{
SOCKET client = accept(s,NULL,NULL) ;
WSAAsyncSelect(client,hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE) ;
printf("接收一个连接\n") ;
}
break ;
case FD_WRITE:
break ;
case FD_READ :
{
char szText[1024] = {0} ;
if(recv(s,szText,1024,0) == SOCKET_ERROR)
{
closesocket(s) ;
}
else
{
printf("接收数据:%s",szText) ;
}
break ;
}
case FD_CLOSE :
{
printf("关闭一个连接\n") ;
closesocket(s);
}
break ;
}
return 0 ;
}
case WM_DESTROY :
PostQuitMessage(0) ;
return 0 ;
}
//将我们不处理的消息交给系统做默认处理
return DefWindowProc(hWnd,uMsg,wParam,lParam) ;
}