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

相比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;
}


扫描二维码关注公众号,回复: 1678444 查看本文章


猜你喜欢

转载自blog.csdn.net/nightwizard2030/article/details/77679725