C++ SOCKET通信模型(二)IOEvent

本系列我准备做一个完整的研究,包含Windows的IOCP IOEvent,linux的poll 和 epoll,上篇已经说了非阻塞IO衍化的select多路复用IO,由于windows和linux都差不多,就不再多说select模型了。那么还是先从windows开始吧,这篇主要看下IOEvent。虽然windows作为服务器没有linux那么强,但还是不得不佩服微软的设计思路,这种模型看起来没有任何多余的损耗,可惜也就是因为微软帮开发人员做得太细,导致了一些问题,具体的以后的文章会说到。IOEvent 用的是Overlapped IO模型,Overlapped IO不仅仅用于网络通信中,包括文件的读写都可能用到。使用起来也比较简单,仅需要将fd buf overlapped(event) 这4者绑在一起,由内核完成数据的收发,并通过event 通知应用层,应用层再进行数据操作已经完成后的操作,不需要多余的内存拷贝,数据已经在指定的buf上了。比较可惜的是,由于操作系统的机制问题,WSAWaitForMultipleEvents 仅仅能监听64个事件,就是说1个线程最多同时处理64个socket,不过这也不是什么问题,多建立几个线程即可,没有其他人说的那么夸张,顶多也就多了一些额外的上下文切换,具体就一起来看看代码吧

server:

// IOEvent.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include
#include
#include
#include
#include 
#include 
#pragma comment(lib,"ws2_32.lib")
unsigned int WINAPI CreateServ(LPVOID args);
unsigned int WINAPI Proc(LPVOID args);
using namespace std;
const int _thread_count = 32;
const int _bufLen = 1024;

struct Client
{
	SOCKET s;
	WSABUF buf;
	WSAOVERLAPPED overlapped;
};

DWORD dwRecvCount = 0;
DWORD nFlag = 0;
int _nIndex[_thread_count]={0};
WSAEVENT _eventList[_thread_count][64];
Client* _clients[_thread_count][64]={0};
mutex m[_thread_count];

int main()
{
	_beginthreadex(0, 0, CreateServ, 0, 0, 0);
	for (int i = 0; i<_thread_count; i++)
	{
		int* temp = new int(i);
		_beginthreadex(0, 0, Proc, temp, 0, 0);
	}
	cin.get();
	cin.get();
	return 0;
}


unsigned int WINAPI Proc(LPVOID args)
{
	int* I = (int*)args;
	int nIndex = 0;
	while (true)
	{
		while (_nIndex[*I] == 0)
		{
			Sleep(500);
		}
		nIndex = WSAWaitForMultipleEvents(_nIndex[*I], _eventList[*I], false, WSA_INFINITE,false);
		if (nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
			continue;
		nIndex = nIndex - WSA_WAIT_EVENT_0;
		cout << "proc by:" << *I << endl;
		list removeList;
		for(int i=nIndex;i<64;i++)
		{
			if(_clients[i]==0)
			{
				break;
			}
			int val=WSAWaitForMultipleEvents(1, &_eventList[*I][i], true, 0, false);
			if (val == WSA_WAIT_FAILED || val == WSA_WAIT_TIMEOUT)
				continue;
			WSAResetEvent(_eventList[*I][i]);
			DWORD retval;
			WSAGetOverlappedResult(_clients[*I][i]->s, &_clients[*I][i]->overlapped, &retval, true, &nFlag);
			if (retval == 0)
			{
				removeList.push_back(i);
			}
			else
			{
				//cout << _clients[*I][i]->buf.buf << endl;
				memset(_clients[*I][i]->buf.buf, 0, _bufLen);
				WSARecv(_clients[*I][i]->s, &_clients[*I][i]->buf, 1, &dwRecvCount, &nFlag, &_clients[*I][i]->overlapped, 0);
				char buf[128];
				sprintf_s(buf, "hello client");
				send(_clients[*I][i]->s, buf, 128, 0);
			}
		}
		removeList.sort([](const int a1,const int a2) {
			return a1 > a2;
		});
		m[*I].lock();
		for(auto iter=removeList.begin();iter!=removeList.end();++iter)
		{
			cout << "release" << endl;
			Client* c = _clients[*I][*iter];
			c->overlapped.hEvent = 0;
			WSACloseEvent(_eventList[*I][*iter]);
			closesocket(c->s);
			if (_nIndex[*I] > 1)
			{				
				_clients[*I][*iter] = _clients[*I][_nIndex[*I] - 1];
				_clients[*I][_nIndex[*I] - 1] = 0;
				_eventList[*I][*iter] = _eventList[*I][_nIndex[*I] - 1];
				_eventList[*I][_nIndex[*I] - 1] = 0;
			}
			else
			{
				_eventList[*I][*iter] = 0;
				_clients[*I][_nIndex[*I]-1] = 0;
			}
			delete[] c->buf.buf;
			delete c;
			_nIndex[*I] -= 1;
		}
		m[*I].unlock();
	}
}
unsigned int WINAPI CreateServ(LPVOID args) {
	srand(time(0));
	WORD wVersion;
	WSADATA wsaData;
	int err;
	wVersion = MAKEWORD(2, 1);
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return 0;
	}
	SOCKET sockSrv = WSASocket(AF_INET, SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);
	const char chOpt = 1;
	setsockopt(sockSrv, IPPROTO_TCP, TCP_NODELAY, &chOpt, sizeof(chOpt));

	int nSendBufLen = 16 * 1024 * 1024;
	setsockopt(sockSrv, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBufLen, sizeof(int));

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(ADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6001);

	::bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	err = listen(sockSrv, 100);
	if (err == SOCKET_ERROR) {
		cout << "listen failed" << endl;
		WSACleanup();
		return 0;
	}

	SOCKADDR_IN remoteAddr;
	int addrSize = sizeof(remoteAddr);
	//accept loop
	
	while (true) {
		SOCKET s = accept(sockSrv, (SOCKADDR*)&remoteAddr, &addrSize);
		Client* c = new Client;
		c->s = s;
		char*  buf = new char[_bufLen];
		memset(buf, 0, _bufLen);
		c->buf.buf = buf;
		c->buf.len = _bufLen;

		int i;
		do
		{
			i = rand() % _thread_count;
		} while (_nIndex[i] >= 64);


		m[i].lock();
		_clients[i][_nIndex[i]] = c;
		_eventList[i][_nIndex[i]] = WSACreateEvent();
		c->overlapped.hEvent = _eventList[i][_nIndex[i]];
		WSARecv(s, &c->buf, 1, &dwRecvCount,&nFlag, &c->overlapped,0);
		_nIndex[i] = _nIndex[i] + 1;
		m[i].unlock();
	}
}

client:

与 C++ SOCKET通信模型(一) 相同,这里就不再重复粘贴出来了



你可能感兴趣的:(C/C++)