您的位置:首页 > 编程语言 > C语言/C++

C++ SOCKET通信模型(四)IOCP

2017-08-29 17:23 295 查看
相比IOEvent,IOCP没有事件监听,而是采用线程池管理(其实就是对用户创建的线程的一个分配管理机制,本身并不会创建多余额外的线程)+队列的形式,这个系统队列也就是所谓的完成端口,用于内核与应用层的交互。IOCP没有监听事件数量的限制,没有事件列表为空需要等待的问题,虽然IOEvent没有IOApc里负载均衡问题严重,但还是存在一定负载均衡问题,并且需要自己去做均衡策略,在IOCP里得到完美解决,采用线程间争抢的模式,这是真正意义上的做到了均衡,后面EPOLL我也会用这个策略去做。相比IOApc,上篇所提到的IOApc的5个问题都得到解决。并且可以看出,IOCP更贴近于IOEvent,APC不合理之处实在太多,所以以后也要尽量避免APC的设计使用。

先说说IOCP的设计思路,不知道有没人在看IOEvent的时候曾想过,有没办法绕过事件监听,并且保持大体结构几乎不变。没错,如果能介入wsarecv的设计的话,可以在通知事件那地方,将一个标识放入队列,然后让所有等待队列的线程去争抢这个标识,那么一定是谁闲谁得到这个标识,一直循环,直到队列为空,该线程执行完后又保持空闲状态,并且配合wsarecv这个异步方法,不再需要额外线程,就可以实现整个循环,真的是把线程上的资源用得淋漓尽致。可能说得还比较粗糙,具体的话还需要读者个人去研究体会。

我写的这个代码也是以简洁为前提,所以发送部分都写的同步模式,如果需要用异步发送的话,需要在Client结构中增加个状态标识,并对发送 接收进行区分,一起在Proc中判断并处理。非调试请关闭服务器端输出,不然会执行得很慢

至此,Windows上socket通信模型 先告一段落,附上整个工程下载地址:
https://pan.baidu.com/s/1slt18Jj
// IOCP.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include<WinSock2.h>
#include<process.h>
#include<mutex>
#include <deque>
#include <map>
#pragma comment(lib,"ws2_32.lib")
unsigned int WINAPI CreateServ(LPVOID args);
unsigned int WINAPI Proc(LPVOID args);
using namespace std;
int _thread_count;
char buf[128];
const int _bufLen = 1024;

struct Client
{
WSAOVERLAPPED overlapped;
SOCKET s;
WSABUF buf;
int procId;
int id;
};

HANDLE hCompPort;
DWORD dwRecvCount = 0;
DWORD nFlag = 0;
map<int, Client*> _clients;
mutex m;
int main()
{
sprintf_s(buf, "hello client");
hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
_beginthreadex(0, 0, CreateServ, 0, 0, 0);
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
_thread_count = sysInfo.dwNumberOfProcessors * 2;
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;
}
void release(Client* c)
{
m.lock();
_clients.erase(c->id);
m.unlock();

cout << "release" << endl;
closesocket(c->s); //关闭套接字
delete[] c->buf.buf;
delete c;
}
unsigned int WINAPI Proc(LPVOID args)
{
int I = *(int*)args;
while (true)
{
Client* c;
DWORD dwTransferred;
LPWSAOVERLAPPED overlapped;
if(GetQueuedCompletionStatus(hCompPort, &dwTransferred, (PULONG_PTR)&c, &overlapped,INFINITE))
{
if(dwTransferred==0)
{
release(c);
continue;
}
//cout << "proc by:" << I<<endl;
//cout << c->buf.buf << endl;
memset(c->buf.buf, 0, _bufLen);

send(c->s, buf, 128, 0);

if (WSARecv(c->s, &c->buf, 1, &dwRecvCount, &nFlag, &c->overlapped, 0) == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
release(c);
}
}
}
else
{
release(c);
}
}
}

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, SOMAXCONN);
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;
memset(c, 0, sizeof(Client));
c->s = s;

char*  buf = new char[_bufLen];
memset(buf, 0, _bufLen);
c->buf.buf = buf;
c->buf.len = _bufLen;

m.lock();
int id;
do
{
id = rand() % MAXINT32;
} while (_clients.find(id) != _clients.end());
_clients.insert(pair<int, Client*>(id, c));
c->id = id;
m.unlock();

if(CreateIoCompletionPort((HANDLE)c->s,hCompPort,(ULONG_PTR)c,0)==0)
{
continue;
}
if(WSARecv(c->s, &c->buf, 1, &dwRecvCount, &nFlag, &c->overlapped, 0)==SOCKET_ERROR)
{
int err = WSAGetLastError();
if(err!=WSA_IO_PENDING)
{
release(c);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ Socket