http://www.cnblogs.com/NeuqUstcIim/archive/2008/08/14/1268185.htmlWSAAsynSelect模型也是一個常用的異步I/O模型。應用程序可以在一個套接字上接收以WINDOWS消息為基礎的網絡事件通知。該模型的實現方法是通過調用WSAAsynSelect函數自動將套接字設置(轉變)為非阻塞模式,并向WINDOWS注冊一個或多個網絡事件,并提供一個通知時使用的窗口句柄。當注冊的事件發生時,對應的窗口將收到一個基于消息的通知。
1 #include <winsock.h>
2 #include <tchar.h>
3
4 #define PORT 5150
5 #define MSGSIZE 1024
6 #define WM_SOCKET WM_USER+0
7
8 #pragma comment(lib, "ws2_32.lib")
9
10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
11
12 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
13 {
14 static TCHAR szAppName[] = _T("AsyncSelect Model");
15 HWND hwnd ;
16 MSG msg ;
17 WNDCLASS wndclass ;
18
19 wndclass.style = CS_HREDRAW | CS_VREDRAW ;
20 wndclass.lpfnWndProc = WndProc ;
21 wndclass.cbClsExtra = 0 ;
22 wndclass.cbWndExtra = 0 ;
23 wndclass.hInstance = hInstance ;
24 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
25 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
26 wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
27 wndclass.lpszMenuName = NULL ;
28 wndclass.lpszClassName = szAppName ;
29
30 if (!RegisterClass(&wndclass))
31 {
32 MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
33 return 0 ;
34 }
35
36 hwnd = CreateWindow (szAppName, // window class name
37 TEXT ("AsyncSelect Model"), // window caption
38 WS_OVERLAPPEDWINDOW, // window style
39 CW_USEDEFAULT, // initial x position
40 CW_USEDEFAULT, // initial y position
41 CW_USEDEFAULT, // initial x size
42 CW_USEDEFAULT, // initial y size
43 NULL, // parent window handle
44 NULL, // window menu handle
45 hInstance, // program instance handle
46 NULL) ; // creation parameters
47
48 ShowWindow(hwnd, iCmdShow);
49 UpdateWindow(hwnd);
50
51 while (GetMessage(&msg, NULL, 0, 0))
52 {
53 TranslateMessage(&msg) ;
54 DispatchMessage(&msg) ;
55 }
56
57 return msg.wParam;
58 }
59
60 LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
61 {
62 WSADATA wsd;
63 static SOCKET sListen;
64 SOCKET sClient;
65 SOCKADDR_IN local, client;
66 int ret, iAddrSize = sizeof(client);
67 char szMessage[MSGSIZE];
68
69 switch (message)
70 {
71 case WM_CREATE:
72 // Initialize Windows Socket library
73 WSAStartup(0x0202, &wsd);
74
75 // Create listening socket
76 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
77
78 // Bind
79 local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
80 local.sin_family = AF_INET;
81 local.sin_port = htons(PORT);
82 bind(sListen, (struct sockaddr *)&local, sizeof(local));
83
84 // Listen
85 listen(sListen, 3);
86
87 // Associate listening socket with FD_ACCEPT event
88 WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT);
89 return 0;
90
91 case WM_DESTROY:
92 closesocket(sListen);
93 WSACleanup();
94 PostQuitMessage(0);
95 return 0;
96
97 case WM_SOCKET:
98 if (WSAGETSELECTERROR(lParam))//lParam的高字節包含了可能出現的任何的錯誤代碼
99 {
100 closesocket(wParam);
101 break;
102 }
103
104 switch (WSAGETSELECTEVENT(lParam)) //lParam的低字節指定已經發生的網絡事件
105 {
106 case FD_ACCEPT:
107 // Accept a connection from client
108 sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize);
109
110 // Associate client socket with FD_READ and FD_CLOSE event
111 WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_CLOSE);
112 break;
113
114 case FD_READ:
115 ret = recv(wParam, szMessage, MSGSIZE, 0);
116
117 if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
118 {
119 closesocket(wParam);
120 }
121 else
122 {
123 szMessage[ret] = '\0';
124 send(wParam, szMessage, strlen(szMessage), 0);
125 }
126 break;
127
128 case FD_CLOSE:
129 closesocket(wParam);
130 break;
131 }
132 return 0;
133 }
134
135 return DefWindowProc(hwnd, message, wParam, lParam);
136 }
WSAAsyncSelect是最簡單的一種Winsock I/O模型(之所以說它簡單是因為一個主線程就搞定了)。使用Raw Windows API寫過窗口類應用程序的人應該都能看得懂。這里,我們需要做的僅僅是:
1.在WM_CREATE消息處理函數中,初始化Windows Socket library,創建監聽套接字,綁定,監聽,并且調用WSAAsyncSelect函數表示我們關心在監聽套接字上發生的FD_ACCEPT事件;
2.自定義一個消息WM_SOCKET,一旦在我們所關心的套接字(監聽套接字和客戶端套接字)上發生了某個事件,系統發送消息(WM_SOCKET)給hWnd指向的窗體,而WndProc函數處理所有發往窗體的消息并且message參數被設置為WM_SOCKET;
3.在WM_SOCKET的消息處理中,分別對FD_ACCEPT、FD_READ和FD_CLOSE事件進行處理;
4.在窗口銷毀消息(WM_DESTROY)的處理函數中,我們關閉監聽套接字,清除Windows Socket library
下面這張用于WSAAsyncSelect函數的網絡事件類型表可以讓你對各個網絡事件有更清楚的認識:
表1
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 應用程序想接收針對套接字的協議家族,本地地址列表發生變化的通知