Windows IO模型之事件模型

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,如果有多个网络事件发生,该函数也只返回一个事件对象索
引,并且该事件是在事件句柄数组中最前面的一个。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值