IOCP的笔记及理解

WIN下最好的网络模型可能就是IOCP完成端口了吧

经过几天的研究可以使用以下比喻来理解完成端口,

完成端口中的完成表示IO操作已经完成后才通知程序,完成端口可以更形象的想象成是完成队列

这里我将完成队列想象成一个管道。

网络库初始化部分省略

第一步:首先主负责人(主线程)在一个房子(服务器)里建立一个管道

程序实现

HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);

第二步:然后再安排N个工人(工作线程)去管道的末端让他们去等待管道了有数据流出,然后再处理这些数据(工作线程函数)

程序实现

SYSTEM_INFO si;
GetSystemInfo(&si);
DWORD dwThreadCnt = si.dwNumberOfProcessors * 2;
for(DWORD i = 0; i < dwThreadCnt; i++)
{
	DWORD dwThreadId;
	HANDLE hThread = CreateThread(NULL,0,WorkerThread,(LPVOID)hIOCP,0,&dwThreadId);
	cout<<"创建工作线程["<<i<<"]"<<endl;
	CloseHandle(hThread);
}
第三步 :然后给房子(服务器)开一扇窗户(端口),监视(监听),并设置一个队伍,让外来连接排队等候

程序实现

SOCKADDR_IN saServer;
saServer.sin_family = AF_INET;
saServer.sin_port = htons(PORT);
saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

SOCKET sListen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

bind(sListen,SOCKADDR*)&saServer,sizeof(saServer));

listen(sServer,SOMAXCONN);

第四步:按队列顺序来循环等待外来连接,当有连接时就接受连接,然后将新来连接的一些信息(j即自定义的PER_HEADER_DATA结构包括socker和SOCKADDR_IN)作为关键值与管道(完成端口)进行绑定,并且告诉他在管道末端有人在等待接受你的IO请求,同时给他一个箱子用来保存完成IO请求的信息(自定的PER_IO_DATA),在这里我使用的是WSARecv请求

一直等到关闭这个窗户,通知这些被安排的工人结束工作(停止工作线程)

程序实现

while(1)
{
	SOCKADDR_IN saClient;
	int sLen = sizeof(saClient);
	SOCKET sClient = accept(sServer,(SOCKADDR*)&saClient,&sLen);
	cout<<"接受到来自"<<inet_ntoa(saClient.sin_addr)<<" : "<<ntohl(saClient.sin_port)<<"的连接"<<endl;

	LPPER_HANDLE_DATA pHandleData = new PER_HANDLE_DATA;
	pHandleData->socket = sClient;
	memcpy(&(pHandleData->sockaddr),&saClient,sizeof(saClient));
	CreateIoCompletionPort((HANDLE)sClient,hIOCP,(ULONG_PTR)pHandleData,0);

	LPPER_IO_DATA pIoData = new PER_IO_DATA;
	ZeroMemory(pIoData,sizeof(PER_IO_DATA));
	pIoData->wsaBuf.buf = pIoData->szBuf;
	pIoData->wsaBuf.len = MAX_BUFF_SIZE;
	DWORD dwSize;
	DWORD dwFlags = 0;
	WSARecv(sClient,&(pIoData->wsaBuf),1,&dwSize,&dwFlags,&(pIoData->overlapped),NULL);
}

DWORD dwByteTrans = 0;
PostQueuedCompletionStatus(hIOCP,dwByteTrans,0,0);
closesocket(sServer);
return 0;


第五部:工人的工作流程(工作线程)

工人们轮流等待管道中有数据流出(等待IOCP队列有完成操作),当有数据从管道中流出时,工人们通过流出的一个箱子和一个关键值来获取这次数据的信息

从关键值(PER_HANDLE_DATA)中就可以获取信息是来自哪一个连接,从信息箱子中可以获取到此次数据是哪一种IO操作,即它的一些数据信息,

处理完这些信息之后,又告诉这个连接说他们在管道末端已经开始准备你的下一次IO操作了

代码实现

DWORD WINAPI WorkerThread(LPVOID lpParameter)
{
	
	HANDLE hIocp = (HANDLE)lpParameter;	

	while(1)
	{
		DWORD dwByte = 0;
		PER_HANDLE_DATA *pHandleData = NULL;
		PER_IO_DATA *pIoData = NULL;

		if( 0 == GetQueuedCompletionStatus(hIocp,&dwByte,(ULONG_PTR*)&pHandleData,(LPOVERLAPPED*)&pIoData,INFINITE))
		{
			if((GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED))
			{
				cout<<"closing socket"<<inet_ntoa(pHandleData->sockaddr.sin_addr)<<endl;
				closesocket(pHandleData->socket);
				delete pHandleData;
				delete pIoData;
				continue;
			}
			else
			{
				cout<<"GetQueueCompletionStatus失败,错误代码:"<<GetLastError()<<endl;
				return 0;
			}
		}
		
		//说明客户端已经退出
		if(dwByte == 0)
		{
			cout<<"closing socket"<<inet_ntoa(pHandleData->sockaddr.sin_addr)<<endl;
			closesocket(pHandleData->socket);
			delete pHandleData;
			delete pIoData;
			continue;
		}

		//处理相关数据
		cout<<"接受来自"<<inet_ntoa(pHandleData->sockaddr.sin_addr)<<"的消息:"<<pIoData->wsaBuf.buf<<endl;

		//继续投递下一次WSARecv操作
		DWORD dwFlags = 0;
		DWORD dwByteRecv = 0;
		ZeroMemory(pIoData,sizeof(PER_IO_DATA));
		pIoData->wsaBuf.buf = pIoData->szBuf;
		pIoData->wsaBuf.len = MAX_BUFF_SIZE;
		WSARecv(pHandleData->socket,&(pIoData->wsaBuf),1,&dwByteRecv,&dwFlags,&(pIoData->overlapped),NULL);
	}
	return 0;
}













  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值