http://www.cnblogs.com/NeuqUstcIim/archive/2008/08/14/1268185.htmlWSAAsynSelect模型也是一個常用的異步I/O模型。應(yīng)用程序可以在一個套接字上接收以WINDOWS消息為基礎(chǔ)的網(wǎng)絡(luò)事件通知。該模型的實(shí)現(xiàn)方法是通過調(diào)用WSAAsynSelect函數(shù)自動將套接字設(shè)置(轉(zhuǎn)變)為非阻塞模式,并向WINDOWS注冊一個或多個網(wǎng)絡(luò)事件,并提供一個通知時(shí)使用的窗口句柄。當(dāng)注冊的事件發(fā)生時(shí),對應(yīng)的窗口將收到一個基于消息的通知。
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的高字節(jié)包含了可能出現(xiàn)的任何的錯誤代碼
99 {
100 closesocket(wParam);
101 break;
102 }
103
104 switch (WSAGETSELECTEVENT(lParam)) //lParam的低字節(jié)指定已經(jīng)發(fā)生的網(wǎng)絡(luò)事件
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模型(之所以說它簡單是因?yàn)橐粋€主線程就搞定了)。使用Raw Windows API寫過窗口類應(yīng)用程序的人應(yīng)該都能看得懂。這里,我們需要做的僅僅是:
1.在WM_CREATE消息處理函數(shù)中,初始化Windows Socket library,創(chuàng)建監(jiān)聽套接字,綁定,監(jiān)聽,并且調(diào)用WSAAsyncSelect函數(shù)表示我們關(guān)心在監(jiān)聽套接字上發(fā)生的FD_ACCEPT事件;
2.自定義一個消息WM_SOCKET,一旦在我們所關(guān)心的套接字(監(jiān)聽套接字和客戶端套接字)上發(fā)生了某個事件,系統(tǒng)發(fā)送消息(WM_SOCKET)給hWnd指向的窗體,而WndProc函數(shù)處理所有發(fā)往窗體的消息并且message參數(shù)被設(shè)置為WM_SOCKET;
3.在WM_SOCKET的消息處理中,分別對FD_ACCEPT、FD_READ和FD_CLOSE事件進(jìn)行處理;
4.在窗口銷毀消息(WM_DESTROY)的處理函數(shù)中,我們關(guān)閉監(jiān)聽套接字,清除Windows Socket library
下面這張用于WSAAsyncSelect函數(shù)的網(wǎng)絡(luò)事件類型表可以讓你對各個網(wǎng)絡(luò)事件有更清楚的認(rèn)識:
表1
FD_READ 應(yīng)用程序想要接收有關(guān)是否可讀的通知,以便讀入數(shù)據(jù)
FD_WRITE 應(yīng)用程序想要接收有關(guān)是否可寫的通知,以便寫入數(shù)據(jù)
FD_OOB 應(yīng)用程序想接收是否有帶外(OOB)數(shù)據(jù)抵達(dá)的通知
FD_ACCEPT 應(yīng)用程序想接收與進(jìn)入連接有關(guān)的通知
FD_CONNECT 應(yīng)用程序想接收與一次連接或者多點(diǎn)join操作完成的通知
FD_CLOSE 應(yīng)用程序想接收與套接字關(guān)閉有關(guān)的通知
FD_QOS 應(yīng)用程序想接收套接字“服務(wù)質(zhì)量”(QoS)發(fā)生更改的通知
FD_GROUP_QOS 應(yīng)用程序想接收套接字組“服務(wù)質(zhì)量”發(fā)生更改的通知(現(xiàn)在沒什么用處,為未來套接字組的使用保留)
FD_ROUTING_INTERFACE_CHANGE 應(yīng)用程序想接收在指定的方向上,與路由接口發(fā)生變化的通知
FD_ADDRESS_LIST_CHANGE 應(yīng)用程序想接收針對套接字的協(xié)議家族,本地地址列表發(fā)生變化的通知