用异步选择模型,应用程序可在一个套接字上,接收以Windows消息为基础的网络事件通知。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。 


服务器端代码: 




C++代码  ​Socket I/O模型之异步选择(WSAAsyncSelect)(转)_ico


  1. // write by larry  
  2. // 2009-8-20  
  3. // This is a server using WSAAsyncSelect model.  
  4. #include "stdafx.h"  
  5. #include <winsock.h>  
  6. #include <tchar.h>  
  7. #define PORT  5150  
  8. #define MSGSIZE  1024  
  9. #define WM_SOCKET  WM_USER+0  
  10. #pragma comment(lib, "ws2_32.lib")  
  11. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);  
  12. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)  
  13. {  
  14.     static TCHAR szAppName[] = _T("AsyncSelect Model");  
  15.     HWND hwnd;  
  16.     MSG msg;  
  17.     WNDCLASS wndclass;  
  18.     wndclass.style         = CS_HREDRAW | CS_VREDRAW;  
  19.     wndclass.lpfnWndProc   = WndProc;  
  20.     wndclass.cbClsExtra    = 0;  
  21.     wndclass.cbWndExtra    = 0;  
  22.     wndclass.hInstance     = hInstance;  
  23.     wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);  
  24.     wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);  
  25.     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  
  26.     wndclass.lpszMenuName  = NULL;  
  27.     wndclass.lpszClassName = szAppName;  
  28.     if (!RegisterClass(&wndclass))  
  29.     {  
  30.         MessageBox(NULL, _T("This program requires Windows NT!"), szAppName, MB_ICONERROR);  
  31.         return 0;  
  32.     }  
  33.     hwnd = CreateWindow(szAppName, // window class name  
  34.         _T("AsyncSelect Model"),   // window caption  
  35.         WS_OVERLAPPEDWINDOW,       // window stype  
  36.         CW_USEDEFAULT,             // initial x postion  
  37.         CW_USEDEFAULT,             // initial y postion  
  38.         CW_USEDEFAULT,             // initial x size  
  39.         CW_USEDEFAULT,             // initial y size  
  40.         NULL,                      // parent window handle  
  41.         NULL,                      // window menu handle  
  42.         hInstance,                 // program instance handle  
  43.         NULL);                     // creation parameters  
  44.     ShowWindow(hwnd, nCmdShow);  
  45.     UpdateWindow(hwnd);  
  46.     while (GetMessage(&msg, NULL, 0, 0))  
  47.     {  
  48.         TranslateMessage(&msg);  
  49.         DispatchMessage(&msg);  
  50.     }  
  51.     return msg.wParam;  
  52. }  
  53. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
  54. {  
  55.     WSADATA wsaData;  
  56.     static SOCKET sListen;  
  57.     SOCKET sClient;  
  58.     SOCKADDR_IN local, client;  
  59.     int ret, iAddrSize = sizeof(client);  
  60.     char szMessage[MSGSIZE];  
  61.     switch (message)  
  62.     {  
  63.     case WM_CREATE:  
  64.         // Initialize windows socket library  
  65.         WSAStartup(0x0202, &wsaData);  
  66.         // Create listening socket  
  67.         sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  68.         // Bind  
  69.         local.sin_family = AF_INET;  
  70.         local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  71.         local.sin_port = htons(PORT);  
  72.         bind(sListen, (sockaddr*)&local, sizeof(SOCKADDR_IN));  
  73.         // Listen  
  74.         listen(sListen, 3);  
  75.         // Associate listening socket with FD_ACCEPT event  
  76.         WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);  
  77.         return 0;  
  78.     case WM_DESTROY:  
  79.         closesocket(sListen);  
  80.         WSACleanup();  
  81.         PostQuitMessage(0);  
  82.         return 0;  
  83.     case WM_SOCKET:  
  84.         if (WSAGETSELECTERROR(lParam))  
  85.         {  
  86.             closesocket(wParam);  
  87.             break;  
  88.         }  
  89.         switch (WSAGETSELECTEVENT(lParam))  
  90.         {  
  91.         case FD_ACCEPT:  
  92.             // Accept a connection from client  
  93.             sClient = accept(wParam, (sockaddr*)&client, &iAddrSize);  
  94.             // Associate client socket with FD_READ and FD_CLOSE event  
  95.             WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_CLOSE);  
  96.             break;  
  97.         case FD_READ:  
  98.             ret = recv(wParam, szMessage, MSGSIZE, 0);  
  99.             if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))  
  100.             {  
  101.                 closesocket(wParam);  
  102.             }  
  103.             else  
  104.             {  
  105.                 szMessage[ret] = '\0';  
  106.                 send(wParam, szMessage, strlen(szMessage), 0);  
  107.             }  
  108.             break;  
  109.         case FD_CLOSE:  
  110.             closesocket(wParam);  
  111.             break;  
  112.         }  
  113.         return 0;  
  114.     }  
  115.     return DefWindowProc(hwnd, message, wParam, lParam);  
  116. }  


服务器端得主要流程: 

1.在WM_CREATE消息处理函数中,初始化Windows Socket library,创建监听套接字,绑定,监听,并且调用WSAAsyncSelect函数表示我们关心在监听套接字上发生的FD_ACCEPT事件; 

2.自定义一个消息WM_SOCKET,一旦在我们所关心的套接字(监听套接字和客户端套接字)上发生了某个事件,系统就会调用WndProc并且message参数被设置为WM_SOCKET; 

3.在WM_SOCKET的消息处理函数中,分别对FD_ACCEPT、FD_READ和FD_CLOSE事件进行处理; 

4.在窗口销毁消息(WM_DESTROY)的处理函数中,我们关闭监听套接字,清除Windows Socket library 


WSAAsyncSelect函数的网络事件类型可以有以下一种: 

FD_READ 应用程序想要接收有关是否可读的通知,以便读入数据 

FD_WRITE 应用程序想要接收有关是否可写的通知,以便写入数据 

FD_OOB 应用程序想接收是否有带外(OOB)数据抵达的通知 

FD_ACCEPT 应用程序想接收与进入连接有关的通知 

FD_CONNECT 应用程序想接收与一次连接或者多点join操作完成的通知 

FD_CLOSE 应用程序想接收与套接字关闭有关的通知 

FD_QOS 应用程序想接收套接字“服务质量”(QoS)发生更改的通知 

FD_GROUP_QOS  应用程序想接收套接字组“服务质量”发生更改的通知(现在没什么用处,为未来套接字组的使用保留) 

FD_ROUTING_INTERFACE_CHANGE 应用程序想接收在指定的方向上,与路由接口发生变化的通知 

FD_ADDRESS_LIST_CHANGE  应用程序想接收针对套接字的协议家族,本地地址列表发生变化的通知