C++ 初识window下 IOCP网络模式

window下 IOCP网络模式

#define WIN32_LEAN_AND_MEAN

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

#include
#pragma comment(lib,"Mswsock.lib")

#include

#define nClient 10
enum IO_TYPE
{
	ACCEPT = 10,
	RECV,
	SEND
};

//数据缓冲区空间大小
#define DATA_BUFF_SIZE 1024
struct IO_DATA_BASE
{
	//重叠体
	OVERLAPPED overlapped;
	//
	SOCKET sockfd;
	//数据缓冲区
	char buffer[DATA_BUFF_SIZE];
	//实际缓冲区数据长度
	int length;
	//操作类型
	IO_TYPE iotype;
};
//向IOCP投递接受连接的任务
void postAccept(SOCKET sockServer, IO_DATA_BASE* pIO_DATA)
{
	pIO_DATA->iotype = IO_TYPE::ACCEPT;
	pIO_DATA->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (FALSE == AcceptEx(sockServer
		, pIO_DATA->sockfd
		, pIO_DATA->buffer
		, 0
		, sizeof(sockaddr_in) + 16
		, sizeof(sockaddr_in) + 16
		, NULL
		, &pIO_DATA->overlapped
	))
	{
		int err = WSAGetLastError();
		if (ERROR_IO_PENDING != err)
		{
			printf("AcceptEx failed with error %d\n", err);
			return;
		}
	}
}
//向IOCP投递接收数据的任务
void postRecv(IO_DATA_BASE* pIO_DATA)
{
	pIO_DATA->iotype = IO_TYPE::RECV;
	WSABUF wsBuff = {};
	wsBuff.buf = pIO_DATA->buffer;
	wsBuff.len = DATA_BUFF_SIZE;
	DWORD flags = 0;
	ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));

	if (SOCKET_ERROR == WSARecv(pIO_DATA->sockfd, &wsBuff, 1, NULL, &flags, &pIO_DATA->overlapped, NULL))
	{
		int err = WSAGetLastError();
		if (ERROR_IO_PENDING != err)
		{
			printf("WSARecv failed with error %d\n", err);
			return;
		}
	}
}
//向IOCP投递发送数据的任务
void postSend(IO_DATA_BASE* pIO_DATA)
{
	pIO_DATA->iotype = IO_TYPE::SEND;
	WSABUF wsBuff = {};
	wsBuff.buf = pIO_DATA->buffer;
	wsBuff.len = pIO_DATA->length;
	DWORD flags = 0;
	ZeroMemory(&pIO_DATA->overlapped, sizeof(OVERLAPPED));

	if (SOCKET_ERROR == WSASend(pIO_DATA->sockfd, &wsBuff, 1, NULL, flags, &pIO_DATA->overlapped, NULL))
	{
		int err = WSAGetLastError();
		if (ERROR_IO_PENDING != err)
		{
			printf("WSASend failed with error %d\n", err);
			return;
		}
	}
}
//-- 用Socket API建立简易TCP服务端
//-- IOCP Server基础流程
int main()
{
	//启动Windows socket 2.x环境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//------------//
	// 1 建立一个socket
	// 当使用socket函数创建套接字时,会默认设置WSA_FLAG_OVERLAPPED标志
	//
	// 注意这里也可以用 WSASocket函数创建socket
	// 最后一个参数需要设置为重叠标志(WSA_FLAG_OVERLAPPED)
	// SOCKET sockServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	//
	SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	// 2.1 设置对外IP与端口信息 
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);//host to net unsigned short
	_sin.sin_addr.s_addr = INADDR_ANY;
	// 2.2 绑定sockaddr与ServerSocket
	if (SOCKET_ERROR == bind(sockServer, (sockaddr*)&_sin, sizeof(_sin)))
	{
		printf("错误,绑定网络端口失败...\n");
	}
	else {
		printf("绑定网络端口成功...\n");
	}

	// 3 监听ServerSocket
	if (SOCKET_ERROR == listen(sockServer, 64))
	{
		printf("错误,监听网络端口失败...\n");
	}
	else {
		printf("监听网络端口成功...\n");
	}
	//-------IOCP Begin-------//

	//4 创建完成端口IOCP
	HANDLE _completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (NULL == _completionPort)
	{
		printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
		return -1;
	}
	//5 关联IOCP与ServerSocket
	//完成键
	auto ret = CreateIoCompletionPort((HANDLE)sockServer, _completionPort, (ULONG_PTR)sockServer, 0);
	if (!ret)
	{
		printf("关联IOCP与ServerSocket失败,错误码=%d\n", GetLastError());
		return -1;
	}

	//6 向IOCP投递接受连接的任务
	IO_DATA_BASE ioData[nClient] = {};
	for (int n = 0; n < nClient; n++)
	{
		postAccept(sockServer, &ioData[n]);
	}

	int msgCount = 0;
	while (true)
	{
		DWORD bytesTrans = 0;
		SOCKET sock = INVALID_SOCKET;
		IO_DATA_BASE* pIOData;
		//在网络内存中查找可连接的的客户端socket
		if (FALSE == GetQueuedCompletionStatus(_completionPort, &bytesTrans, (PULONG_PTR)&sock, (LPOVERLAPPED*)&pIOData, 1))
		{
			int err = GetLastError();
			if (WAIT_TIMEOUT == err)
			{
				continue;
			}
			if (ERROR_NETNAME_DELETED == err)
			{
				printf("关闭 sockfd=%d\n", pIOData->sockfd);
				closesocket(pIOData->sockfd);
				continue;
			}
			printf("GetQueuedCompletionStatus failed with error %d\n", err);
			break;
		}
		//7.1 接受链接 完成
		if (IO_TYPE::ACCEPT == pIOData->iotype)
		{
			printf("新客户端加入 sockfd=%d\n", pIOData->sockfd);
			//7.2 关联IOCP与ClientSocket
			auto ret = CreateIoCompletionPort((HANDLE)pIOData->sockfd, _completionPort, (ULONG_PTR)pIOData->sockfd, 0);
			if (!ret)
			{
				printf("关联IOCP与ClientSocket=%d失败\n", pIOData->sockfd);
				closesocket(pIOData->sockfd);
				continue;
			}
			//7.3 向IOCP投递接收数据任务
			postRecv(pIOData);
		}
		//8.1 接收数据 完成 Completion
		else if (IO_TYPE::RECV == pIOData->iotype)
		{
			if (bytesTrans <= 0)
			{//客户端断开处理
				printf("关闭 sockfd=%d, RECV bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
				closesocket(pIOData->sockfd);
				continue;
			}
			printf("收到数据: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, ++msgCount);
			pIOData->length = bytesTrans;
			//8.2 向IOCP投递发送数据任务
			postSend(pIOData);
		}
		//9.1 发送数据 完成 Completion
		else if (IO_TYPE::SEND == pIOData->iotype)
		{
			if (bytesTrans <= 0)
			{//客户端断开处理
				printf("关闭 sockfd=%d, SEND bytesTrans=%d\n", pIOData->sockfd, bytesTrans);
				closesocket(pIOData->sockfd);
				continue;
			}
			printf("发送数据: sockfd=%d, bytesTrans=%d msgCount=%d\n", pIOData->sockfd, bytesTrans, msgCount);
			//9.2 向IOCP投递接收数据任务
			postRecv(pIOData);
		}
		else {
			printf("未定义行为 sockfd=%d", sock);
		}
	}

	//------------//
	//10.1 关闭ClientSocket
	for (int n = 0; n < nClient; n++)
	{
		closesocket(ioData[n].sockfd);
	}
	//10.2 关闭ServerSocket
	closesocket(sockServer);
	//10.3 关闭完成端口
	CloseHandle(_completionPort);
	//清除Windows socket环境
	WSACleanup();
	return 0;
}

 

你可能感兴趣的:(c++)