原文地址
我们为什么要使用Socket I/O模型呢?还得从Socket的阻塞和非阻塞说起。
在网上看过一篇讲解I/O模型的文章,它举过一个例子觉得挺好,那就是收信的例子。
比如:老周在等待他女儿从美国寄过来的信件,老周住三楼,信箱在一楼。有以下几种情况:
第一:老周一直守在信箱旁边,直到收到信件为止。这样太费精力。这就好比是阻塞套接字。
第二:老周到信箱那里看一下,发现还没有来,就马上回家了。这就好比非阻塞套接字。
第三:老周先打个电话到一楼管理员问一下自己的信件是否到了,如果到了才下楼去取信件。当然这样浪费电话费,但是值得的。这可以比作Socket的Select I/O模型。
1
#include
"
stdafx.h
"
2 #include < iostream >
3 #include < winsock2.h >
4 #include < windows.h >
5
6 #define TRACE ATLTrace // 必须要加上这个宏定义,否则在WIN32的控制台程序中是不能直接用的
7
8 #define InternetAddr "127.0.0.1"
9 #define iPort 5055
10
11 #pragma comment(lib, "ws2_32.lib")
12
13 int _tmain( int argc, _TCHAR * argv[])
14 {
15 WSADATA wsa;
16 WORD wVersionRequested;
17 int err;
18
19 wVersionRequested = MAKEWORD( 2 , 2 );
20 err = WSAStartup( wVersionRequested, & wsa);
21 if ( err != 0 ) {
22 // Tell the user that we could not find a usable
23 // WinSock DLL.
24 TRACE( " 你忘记添加WinSock DLL了/n " );
25 WSACleanup();
26 return 1 ;
27 }
28
29 // Create a SOCKET for listening for incoming connection requests
30 SOCKET fdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
31
32 sockaddr_in server;
33
34 server.sin_family = AF_INET;
35 server.sin_addr.s_addr = inet_addr(InternetAddr);
36 server.sin_port = htons(iPort);
37 // Bind the socket.
38 int ret = bind(fdServer, (sockaddr * ) & server, sizeof (server));
39 ret = listen(fdServer, 4 );
40
41 SOCKET AcceptSocket;
42 fd_set fdread;
43 timeval tv;
44 int nSize;
45 // 其实也算是轮训,那么对阻塞socket用select和对使用非阻塞socket的优点在哪?
46 // 可能的优点就是避免在非阻塞套接字里重复检查WSAEWOULDBLOCK错误。
47 while ( 1 )
48 {
49
50 FD_ZERO( & fdread); // 初始化fd_set
51 FD_SET(fdServer, & fdread); // 分配套接字句柄到相应的fd_set
52
53 tv.tv_sec = 2 ; // 这里我们打算让select等待两秒后返回,避免被锁死,也避免马上返回
54 tv.tv_usec = 0 ;
55
56 select( 0 , & fdread, NULL, NULL, & tv);
57
58 nSize = sizeof (server);
59 // 先判断fdServer是否还在fd_set内来判断是否可以读,这样就避免因为 accept在等待
60 // 时造成的阻塞
61 if (FD_ISSET(fdServer, & fdread))
62 // 如果套接字句柄还在fd_set里,说明客户端已经有connect的请求发过来了,
63 // 马上可以accept成功
64 {
65 AcceptSocket = accept(fdServer,( sockaddr * ) & server, & nSize);
66 break ;
67 }
68 else
69 // 还没有客户端的connect请求,我们可以去做别的事,避免像没有用select方式
70 // 的阻塞套接字程序被锁死的情况,如果没用select,当程序运行到accept的时候客户
71 // 端恰好没有connect请求,那么程序就会被锁死,做不了任何事情
72 {
73 // do something
74 MessageBox(NULL, " waiting " , " recv " , MB_ICONINFORMATION);
75 // 别的事做完后,继续去检查是否有客户端连接请求
76 }
77 }
78
79 char buffer[ 128 ];
80 ZeroMemory(buffer, 128 );
81
82 ret = recv(AcceptSocket,buffer, 128 , 0 ); // 这里同样可以用select,用法和上面一样
83
84 MessageBox(NULL, buffer, " recv " , MB_ICONINFORMATION);
85
86 closesocket(AcceptSocket);
87 WSACleanup();
88 return 0 ;
89 }
2 #include < iostream >
3 #include < winsock2.h >
4 #include < windows.h >
5
6 #define TRACE ATLTrace // 必须要加上这个宏定义,否则在WIN32的控制台程序中是不能直接用的
7
8 #define InternetAddr "127.0.0.1"
9 #define iPort 5055
10
11 #pragma comment(lib, "ws2_32.lib")
12
13 int _tmain( int argc, _TCHAR * argv[])
14 {
15 WSADATA wsa;
16 WORD wVersionRequested;
17 int err;
18
19 wVersionRequested = MAKEWORD( 2 , 2 );
20 err = WSAStartup( wVersionRequested, & wsa);
21 if ( err != 0 ) {
22 // Tell the user that we could not find a usable
23 // WinSock DLL.
24 TRACE( " 你忘记添加WinSock DLL了/n " );
25 WSACleanup();
26 return 1 ;
27 }
28
29 // Create a SOCKET for listening for incoming connection requests
30 SOCKET fdServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
31
32 sockaddr_in server;
33
34 server.sin_family = AF_INET;
35 server.sin_addr.s_addr = inet_addr(InternetAddr);
36 server.sin_port = htons(iPort);
37 // Bind the socket.
38 int ret = bind(fdServer, (sockaddr * ) & server, sizeof (server));
39 ret = listen(fdServer, 4 );
40
41 SOCKET AcceptSocket;
42 fd_set fdread;
43 timeval tv;
44 int nSize;
45 // 其实也算是轮训,那么对阻塞socket用select和对使用非阻塞socket的优点在哪?
46 // 可能的优点就是避免在非阻塞套接字里重复检查WSAEWOULDBLOCK错误。
47 while ( 1 )
48 {
49
50 FD_ZERO( & fdread); // 初始化fd_set
51 FD_SET(fdServer, & fdread); // 分配套接字句柄到相应的fd_set
52
53 tv.tv_sec = 2 ; // 这里我们打算让select等待两秒后返回,避免被锁死,也避免马上返回
54 tv.tv_usec = 0 ;
55
56 select( 0 , & fdread, NULL, NULL, & tv);
57
58 nSize = sizeof (server);
59 // 先判断fdServer是否还在fd_set内来判断是否可以读,这样就避免因为 accept在等待
60 // 时造成的阻塞
61 if (FD_ISSET(fdServer, & fdread))
62 // 如果套接字句柄还在fd_set里,说明客户端已经有connect的请求发过来了,
63 // 马上可以accept成功
64 {
65 AcceptSocket = accept(fdServer,( sockaddr * ) & server, & nSize);
66 break ;
67 }
68 else
69 // 还没有客户端的connect请求,我们可以去做别的事,避免像没有用select方式
70 // 的阻塞套接字程序被锁死的情况,如果没用select,当程序运行到accept的时候客户
71 // 端恰好没有connect请求,那么程序就会被锁死,做不了任何事情
72 {
73 // do something
74 MessageBox(NULL, " waiting " , " recv " , MB_ICONINFORMATION);
75 // 别的事做完后,继续去检查是否有客户端连接请求
76 }
77 }
78
79 char buffer[ 128 ];
80 ZeroMemory(buffer, 128 );
81
82 ret = recv(AcceptSocket,buffer, 128 , 0 ); // 这里同样可以用select,用法和上面一样
83
84 MessageBox(NULL, buffer, " recv " , MB_ICONINFORMATION);
85
86 closesocket(AcceptSocket);
87 WSACleanup();
88 return 0 ;
89 }
第四:老周告诉一楼管理员,如果有他的信件就通知老周。这可以比做Socket的WSAAsynSelect模型
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 }
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 }
第五:老周要一楼管理员发个短信到他们家里去。相当如WSAEventSelect模型,其实WSAEventSelect模型类似WSAAsynSelect模型,但最主要的区别是网络事件发生时会被发送到一个事件对象句柄,而不是发送到一个窗口。这样可能更加的好,对于服务器端的程序来说。
1
SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS];
2 WSAEVENT Event[WSA_MAXINUM_WAIT_EVENTS];
3 SOCKET Accept, Listen;
5 DWORD EventTotal = 0 ;
6 DWORD Index;
7
8 // Set up a TCP socket for listening on port 5150
9 Listen = socket(PF_INET,SOCK_STREAM, 0 );
10
11 InternetAddr.sin_family = AF_INET;
12 InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
13 InternetAddr.sin_port = htons( 5150 );
14
15 bind(Listen,(PSOCKADDR) & InternetAddr, sizeof (InternetAddr));
16
17 NewEvent = WSACreateEvent();
18
19 WSAEventSelect(Listen,NewEvnet,FD_ACCEPT | FD_CLOSE);
20
21 listen(Listen, 5 );
22
23 Socket[EventTotal] = Listen;
24 Event[EventTotal] = NewEvent;
25 EventTotal ++ ;
26
27 while (TRUE)
28 {
29 // Wait for network events on all sockets
30 Index = WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
31
32 WSAEnumNewWorkEvents(SocketArray[Index - WSA_WAIT_EVENT_0],
33 EventArray[Index - WSA_WAIT_EVENT_0],
34 & NetworkEvents);
35 // Check for FD_ACCEPT messages
36 if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
37 {
38 if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0 )
39 {
40 // Error
41 break ;
42 }
43 // Accept a new connection and add it to the socket and event lists
44 Accept = accept(SocketArray[Index - WSA_WAIT_EVENT_0],NULL,NULL);
45
46 // We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,
47 // so close the accepted socket
48 if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
49 {
50 printf( " .. " );
51 closesocket (Accept);
52 break ;
53 }
54 NewEvent = WSACreateEvent();
55
56 WSAEventSelect(Accept,NewEvent,FD_READ | FD_WRITE | FD_CLOSE);
57
58 Event[EventTotal] = NewEvent;
59 Socket[EventTotal] = Accept;
60 EventTotal ++ ;
61 prinrt( " Socket %d connect/n " ,Accept);
62 }
63 // Process FD_READ notification
64 if (NetworkEvents.lNetwoAD)rkEvents & FD_RE
65 {
66 if (NetworkEvents.iErrorCode[FD_READ_BIT != 0 ])
67 {
68 // Error
69 break ;
70 }
71
72 // Read data from the socket
73 recv(Socket[Index - WSA_WAIT_EVENT_0],buffer, sizeof (buffer), 0 );
74 }
75 // process FD_WRITE notitication
76 if (NetworkEvents.lNetworkEvents & FD_WRITE)
77 {
78 if (NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0 )
79 {
80 // Error
81 break ;
82 }
83 send(Socket[Index - WSA_WAIT_EVENT_0],buffer, sizeof (buffer), 0 );
84 }
85 if (NetworkEvents.lNetworkEvents & FD_CLOSE)
86 {
87 if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0 )
88 {
89 // Error
90 break ;
91 }
92 closesocket (Socket[Index - WSA_WAIT_EVENT_0]);
93 // Remove socket and associated event from the Socket and Event arrays and
94 // decrement eventTotal
95 CompressArrays(Event,Socket, & EventTotal);
96 }
97 }
2 WSAEVENT Event[WSA_MAXINUM_WAIT_EVENTS];
3 SOCKET Accept, Listen;
5 DWORD EventTotal = 0 ;
6 DWORD Index;
7
8 // Set up a TCP socket for listening on port 5150
9 Listen = socket(PF_INET,SOCK_STREAM, 0 );
10
11 InternetAddr.sin_family = AF_INET;
12 InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
13 InternetAddr.sin_port = htons( 5150 );
14
15 bind(Listen,(PSOCKADDR) & InternetAddr, sizeof (InternetAddr));
16
17 NewEvent = WSACreateEvent();
18
19 WSAEventSelect(Listen,NewEvnet,FD_ACCEPT | FD_CLOSE);
20
21 listen(Listen, 5 );
22
23 Socket[EventTotal] = Listen;
24 Event[EventTotal] = NewEvent;
25 EventTotal ++ ;
26
27 while (TRUE)
28 {
29 // Wait for network events on all sockets
30 Index = WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
31
32 WSAEnumNewWorkEvents(SocketArray[Index - WSA_WAIT_EVENT_0],
33 EventArray[Index - WSA_WAIT_EVENT_0],
34 & NetworkEvents);
35 // Check for FD_ACCEPT messages
36 if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
37 {
38 if (NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0 )
39 {
40 // Error
41 break ;
42 }
43 // Accept a new connection and add it to the socket and event lists
44 Accept = accept(SocketArray[Index - WSA_WAIT_EVENT_0],NULL,NULL);
45
46 // We cannot process more than WSA_MAXIMUM_WAIT_EVENTS sockets ,
47 // so close the accepted socket
48 if (EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
49 {
50 printf( " .. " );
51 closesocket (Accept);
52 break ;
53 }
54 NewEvent = WSACreateEvent();
55
56 WSAEventSelect(Accept,NewEvent,FD_READ | FD_WRITE | FD_CLOSE);
57
58 Event[EventTotal] = NewEvent;
59 Socket[EventTotal] = Accept;
60 EventTotal ++ ;
61 prinrt( " Socket %d connect/n " ,Accept);
62 }
63 // Process FD_READ notification
64 if (NetworkEvents.lNetwoAD)rkEvents & FD_RE
65 {
66 if (NetworkEvents.iErrorCode[FD_READ_BIT != 0 ])
67 {
68 // Error
69 break ;
70 }
71
72 // Read data from the socket
73 recv(Socket[Index - WSA_WAIT_EVENT_0],buffer, sizeof (buffer), 0 );
74 }
75 // process FD_WRITE notitication
76 if (NetworkEvents.lNetworkEvents & FD_WRITE)
77 {
78 if (NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0 )
79 {
80 // Error
81 break ;
82 }
83 send(Socket[Index - WSA_WAIT_EVENT_0],buffer, sizeof (buffer), 0 );
84 }
85 if (NetworkEvents.lNetworkEvents & FD_CLOSE)
86 {
87 if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0 )
88 {
89 // Error
90 break ;
91 }
92 closesocket (Socket[Index - WSA_WAIT_EVENT_0]);
93 // Remove socket and associated event from the Socket and Event arrays and
94 // decrement eventTotal
95 CompressArrays(Event,Socket, & EventTotal);
96 }
97 }
第六:老周可以要求一楼管理员把信件送到他们家去,好比Overlapped I/O 事件通知模型。
第七:老周还可以要求不仅送信件,还可以要求管理员帮他把信封打开,读给老周听(假设老周为文盲,管理员也够累的),这就是Overlapped I/O 完成例程模型 了。