1.Windows I/O模型
1.1事件选择模型
WSAEventSelect 是 WinSock 提供的一种异步事件通知I/O模型,与 WSAAsyncSelect模型有些类似。该模型同样是接收事件( FD_XXX ),通过事件对象句柄通知,而非像 WSAAsyncSelect一样依靠Windows的消息驱动机制。
与WSAAsyncSelect模型相同,WSAEventSelect将所有的SOCKET事件分为如下类型:FD_ALL_EVENTS 代表所有的事件(共十种)
FD_READ , FD_WRITE , FD_OOB , FD_ACCEPT, FD_CONNECT , FD_CLOSE,
FD_QOS , FD_GROUP_QOS , FD_ROUTING_INTERFACE_CHANGE , FD_ADDRESS_LIST_CHANGE
其中 FD_READ 的定义如下:
#define FD_READ_BIT 0
#define FD_READ (1 << FD_READ_BIT) // = 1
其他的定义也都类似,比如: FD_ACCEPT_BIT = 3
在WSAEventSelect模型中,基本流程如下:
1. 创建一个事件对象数组,用于存放所有的事件对象;
2. 创建一个事件对象(WSACreateEvent);
3. 将一组你感兴趣的SOCKET事件与事件对象关联(WSAEventSelect),然后加入事件对象数组;
4. 等待事件对象数组上发生一个你感兴趣的网络事件WSAWaitForMultipleEvents;
5. 对发生事件的事件对象查询具体发生的事件类型(WSAEnumNetworkEvents);
6. 针对不同的事件类型进行不同的处理;
7. 循环进行 》》4
下面是一个例子http://www.voidcn.com/article/p-xjsimbfn-bqe.html
//server
#include <winsock2.h>
#include <cstdio>
#pragma comment(lib, "ws2_32.lib")
#define PORT 5000
#define MSGSIZE 1024
int g_iTotalConn = 0;
SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
DWORD WINAPI WorkerThread(LPVOID);
void Cleanup(int index);
int main() {
// Initialize Windows Socket library
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
SOCKET sListen, sClient;
SOCKADDR_IN local, client;
DWORD dwThreadId;
int iaddrSize = sizeof(SOCKADDR_IN);
// Create listening socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Bind
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
// Listen
listen(sListen, 3);
// Create worker thread
CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
while (TRUE) {
// Accept a connection
sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
// Associate socket with network event
g_CliSocketArr[g_iTotalConn] = sClient;
g_CliEventArr[g_iTotalConn] = WSACreateEvent();
WSAEventSelect(g_CliSocketArr[g_iTotalConn], g_CliEventArr[g_iTotalConn], FD_READ | FD_CLOSE);
g_iTotalConn++;
}
return 0;
}
DWORD WINAPI WorkerThread(LPVOID lpParam) {
int ret, index;
WSANETWORKEVENTS NetworkEvents;
char szMessage[MSGSIZE];
while (TRUE) {
ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT) {
continue;
}
index = ret - WSA_WAIT_EVENT_0;
WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index], &NetworkEvents);
if (NetworkEvents.lNetworkEvents & FD_READ) {
// Receive message from client
ret = recv(g_CliSocketArr[index], szMessage, MSGSIZE, 0);
if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) {
Cleanup(index);
} else {
//send(g_CliSocketArr[index], szMessage, strlen(szMessage), 0);
printf("recv data:%s\n", szMessage);
}
}
if (NetworkEvents.lNetworkEvents & FD_CLOSE) {
Cleanup(index);
}
}
return 0;
}
void Cleanup(int index) {
closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);
if (index < g_iTotalConn - 1) {
g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
}
g_iTotalConn--;
}
Notice:
最多可以支持WSA_MAXIMUM_WAIT_EVENTS个对象,他的大小是64.该函数会等待网络
事件的发生,如果过了指定了时间(dwTimeOut)则返回WSA_WAIT_TIMEOUT;如果在规定
的时间内有事件发生,则返回该事件对象的索引(注意:在程序中要想得到发生的事件的真正
索引需得用返回值减去WSA_WAIT_EVENT_0),调用失败返回WSA_WAIT_FAILED.如果将
参数fWaitAll设置成FALSE,如果有多个网络事件发生,该函数也只返回一个事件对象索
引,并且该事件是在事件句柄数组中最前面的一个。