本系列我准备做一个完整的研究,包含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();
}
}
与 C++ SOCKET通信模型(一) 相同,这里就不再重复粘贴出来了