IOCP 基本模型 事例

送上事例前先回忆一下IOCP的步骤

1、创建一个完完成端口

2、创建一个线程A

3、A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是阻塞函数

4、主线程循环调用accept等待客户端连接上来。

5、主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用。 因为是异步函数,WSASend/WSARecv会马上返回, 实际的发送或者接收数据的操作有WINDOWS系统去做

6、主线程继续下一次循环,阻塞在accept这里等待客户端的连接

7、WINDOWS系统完成WSASend或者WSARecv的擦做, 把结果发送到完成端口。

8、A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

9、在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv , 并继续下一次循环阻塞在GetQueuedCompletionStatues()这里。


归根结底概括完成端口模型一句话:

我们不停的发出异步的WSASend/WSARecv  IO操作,具体的IO处理过程由WINDOWS系统完成, WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了, 那么就在完成端口那里排成一个队列)。我们在另一个线程里从完成端口不断地取得IO操作结果,然后根据需要再发出WSASend/WSARecv IO操作。


以下献上一个不是很完善的例子, 但是是已经可以通讯的了。 简单示意了IOCP模型的基本步骤

服务端


#include <iostream>
#include <winsock2.h>
#include <winsock.h>
using namespace std;

#pragma  comment(lib, "ws2_32.lib")
#pragma  comment(lib, "kernel32.lib")

HANDLE g_hIOCP;

enum IO_OPERATION
{
	IO_READ,
	IO_WRITE
};

struct IO_DATA
{
	OVERLAPPED		Overlapped;
	WSABUF			Wsabuf;
	int				nBytes;
	IO_OPERATION	opCode;
	SOCKET			client;
};

char buffer[1024];


// 这是一个很简单的IOCP通讯。 里面有很多不和逻辑的地方。 仅供参考
DWORD WINAPI WorkerThread(LPVOID WorkThreadContext)
{
	IO_DATA* lpIOContext = NULL;
	DWORD nBytes = 0;
	DWORD dwFlags = 0;
	int nRet = 0;

	DWORD dwIoSize = 0;

	void* lpCompletionKey = NULL;
	LPOVERLAPPED lpOverlapped = NULL;

	while (true)
	{
		// 此处是一个阻塞函数, 当完成端口处理完client发来的数据并通知这个线程,并把数据存储到lpOverlapped里, 继续往下执行
		GetQueuedCompletionStatus(g_hIOCP, &dwIoSize, (LPDWORD)&lpCompletionKey, (LPOVERLAPPED*)&lpOverlapped, INFINITE);

		lpIOContext = (IO_DATA*)lpOverlapped;
		if ( dwIoSize == 0 )
		{
			cout << "Client disonnect" << endl;
			closesocket(lpIOContext->client);
			delete lpIOContext;
			continue;
		}


		if ( lpIOContext->opCode == IO_READ)
		{
			ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
			lpIOContext->Wsabuf.buf = buffer;
			lpIOContext->Wsabuf.len = strlen(buffer) + 1;
			lpIOContext->opCode = IO_WRITE;
			lpIOContext->nBytes = strlen(buffer) + 1;
			dwFlags = 0;
			nBytes = strlen(buffer) + 1;

			nRet = WSASend(
				lpIOContext->client,
				&lpIOContext->Wsabuf, 1, &nBytes, dwFlags,
				&(lpIOContext->Overlapped), NULL);

			if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
			{
				cout << "WSASend failed::Reason Code::" << WSAGetLastError() << endl;
				closesocket(lpIOContext->client);
				delete lpIOContext;
				continue;
			}
			memset(buffer, NULL, sizeof(buffer));

		}else if ( lpIOContext->opCode == IO_WRITE )
		{
			// 当recv一次之后要重新recv 
			lpIOContext->opCode = IO_READ;
			nBytes = 1024;
			dwFlags = 0;
			lpIOContext->Wsabuf.buf = buffer;
			lpIOContext->Wsabuf.len = nBytes;
			lpIOContext->nBytes = nBytes;
			ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));

			// 此处是异步接收消息的
			nRet = WSARecv(
				lpIOContext->client,
				&lpIOContext->Wsabuf, 1, &nBytes,
				&dwFlags, &lpIOContext->Overlapped, NULL);

			if ( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
			{
				cout << "WSARecv Failed::Reason Code1::" << WSAGetLastError() << endl;
				closesocket(lpIOContext->client);
				delete lpIOContext;
				continue;
			}
			// 这个打印有点问题, 可能是因为异步的问题  这里的buf打印的是错误的
			cout << lpIOContext->Wsabuf.buf << endl;
		}

	}

	return 0;
}

void main()
{
	/*
		为了在应用程序当中调用任何一个WinSock API函数, 首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化
		此步作用就是完成对Winsock服务的初始化
	*/
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	// 创建套接字
	SOCKET m_socket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

	sockaddr_in server;
	server.sin_family = AF_INET;
	server.sin_port = htons(6000);
	server.sin_addr.S_un.S_addr = htons(INADDR_ANY);

	bind(m_socket, (sockaddr*)&server, sizeof(server));

	listen(m_socket, 8);

	SYSTEM_INFO sysInfo;
	GetSystemInfo(&sysInfo);
	int g_ThreadCount = sysInfo.dwNumberOfProcessors * 2;

	g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, g_ThreadCount);

	for (int i = 0; i < g_ThreadCount; ++i )
	{
		HANDLE hThread;
		DWORD dwThreadId;
		hThread = CreateThread(NULL, 0, WorkerThread, 0, 0, &dwThreadId);
		
		CloseHandle(hThread);
	}

	while (true)
	{
		// 监听服务端的socket 如果监听到将监听到的socket返回出来
		SOCKET client = accept(m_socket, NULL, NULL);
		cout << "Client connected." << endl;

		// 将监听到的客户端的socket与完成端口关联起来。
		if ( CreateIoCompletionPort((HANDLE)client, g_hIOCP, 0, 0) == NULL)
		{
			cout << "Binding Client Socket to IO Completion Port Failed::Reason Code :: " << GetLastError() << endl;
			closesocket(client);
		}
		else
		{
			IO_DATA* data = new IO_DATA;
			memset(buffer, NULL, sizeof(buffer));
			memset(&data->Overlapped, 0, sizeof(data->Overlapped));
			data->opCode = IO_READ;
			data->nBytes = 0;
			data->Wsabuf.buf = buffer;
			data->Wsabuf.len = sizeof(buffer);
			data->client = client;
			DWORD nBytes = 1024, dwFlags = 0;

			// 此处是异步接收消息的,  让关联的客户端发来消息时为给Wsabuf内复制,并通知完成端口。 完成端口会把处理的结果发送到线程。
			int nRet = WSARecv(client, &data->Wsabuf, 1, &nBytes, &dwFlags, &data->Overlapped, NULL);
			if ( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
			{
				cout << "WSARecv Failed::Reason Code::" << WSAGetLastError() << endl;
				closesocket(client);
				delete data;
			}
			// 这个打印有点问题, 可能是因为异步的问题  这里的buf打印的是错误的
			cout << data->Wsabuf.buf << endl;
		}

	}

	closesocket(m_socket);
	WSACleanup();


}

客户端:

#include <iostream>
#include <WinSock2.h>
#include <string.h>
using namespace std;

#pragma  comment(lib, "ws2_32.lib")

void main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	sockaddr_in server;
	server.sin_family = AF_INET;
	server.sin_port = htons(6000);
	server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	int flag;
	flag = connect(client, (sockaddr*)&server, sizeof(server));
	if ( flag < 0 )
	{
		cout << "error!" << endl;
		return;
	}
	while (true)
	{
		cout << "sent hello!!!" << endl;
		char buffer[1024];
		//strcpy(buffer, "hello");
		strcpy_s(buffer, 10, "hello");
		send(client, buffer, 1024, 0);

		memset(buffer, NULL, sizeof(buffer));

		cout << "recv" << endl;

		int rev = recv(client, buffer, 1024, 0);
		if ( rev == 0 )
		{
			cout << "recv nothing!" << endl;
		}
		cout << buffer << endl;
		Sleep(1000);
	}

	closesocket(client);
	WSACleanup();
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值