Windows上io复用-完成端口(iocp)

简单认识iocp

#define  _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <Windows.h>

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

#define BUF_SIZE 1024

typedef struct tagPER_HANDLE_DATA
{
	SOCKET socket;
	SOCKADDR_STORAGE clientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

typedef struct tagPER_IO_DATA
{
	OVERLAPPED overlappend;
	WSABUF dataBuf;
	char buffer[1024];
	int bufferLen;
	int OperationType; //读写标志
}PER_IO_DATA, *LPPER_IO_DATA;

DWORD WINAPI ServerWorkFunc(LPVOID lpParam);

int main(int argc, char *argv[])
{

	if (argc < 2)
	{
		printf("agrc < 2\n");
		abort();
	}

	WSADATA wsadata;
	WORD socket_verson = MAKEWORD(2, 2);			//定义版本
	if (WSAStartup(socket_verson, &wsadata) != 0)		//
	{
		fprintf(stderr, "socket init failed!\n");
		abort();
	}
	int port = atoi(argv[1]);
	SOCKADDR_IN server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
	server_addr.sin_port = htons(port);

	HANDLE completionPort;
	SYSTEM_INFO systemInfo;
	SOCKET serverSocket;

	//完成端口
	//1.创建一个完成端口
	completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

	//2.获取cpu个数
	GetSystemInfo(&systemInfo);

	//3.根据cpu个数创建工作线程
	for (int i = 0; i < systemInfo.dwNumberOfProcessors; i++)
	{
		HANDLE threadHandle;
		threadHandle = CreateThread(NULL, 0, ServerWorkFunc, completionPort, 0, NULL);
		CloseHandle(threadHandle);
	}

	//4.创建一个sokcet,并进行绑定和监听
	serverSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	int bind_result = bind(serverSocket, (SOCKADDR *)&server_addr, sizeof(server_addr));
	if (bind_result == SOCKET_ERROR)
	{
		fprintf(stderr, "socket bind failed!\n");
		abort();
	}

	int listen_result = listen(serverSocket, 10);
	if (listen_result == SOCKET_ERROR)
	{
		fprintf(stderr, "socket listen error!\n");
		abort();
	}

	for (;;)
	{
		PER_HANDLE_DATA* perHandleData = NULL;
		SOCKADDR_IN saRemote;
		SOCKET clientSocket;
		int remoteLen;
		remoteLen = sizeof(saRemote);
		clientSocket = accept(serverSocket, (SOCKADDR *)&saRemote, &remoteLen);
		if (clientSocket == SOCKET_ERROR)
		{
			printf("client socket is null\n");
			continue;
		}
		perHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));

		perHandleData->socket = clientSocket;
		memcpy(&perHandleData->clientAddr, &saRemote, remoteLen);

		//7.将socket和完成端口进行关联
		CreateIoCompletionPort((HANDLE)clientSocket, completionPort, (DWORD)perHandleData, 0);


		//开始接受socket 处理i/o
		//使用重叠i/o,在新建的socket投递一个或多个异步
		//WSARecv或WSASend请求,在这些i/o请求完成之后,工作线程会为i/o请求提供服务
		static int DATA_BUFSIZE = 4096;
		DWORD recvBytes = 0;
		DWORD flags = 0;
		LPPER_IO_DATA perIoData = NULL;
		perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
		ZeroMemory(&(perIoData->overlappend), sizeof(OVERLAPPED));
		perIoData->dataBuf.len = 1024;
		perIoData->dataBuf.buf = perIoData->buffer;
		perIoData->OperationType = 0; //读
		WSARecv(perHandleData->socket, 
			&(perIoData->dataBuf), 1, 
			&recvBytes, 
			&flags, 
			&(perIoData->overlappend), 
			NULL);
	}
	closesocket(serverSocket);
	WSACleanup();
	return 0;
}

DWORD WINAPI ServerWorkFunc(LPVOID lpParam)
{
	HANDLE completionPort = (HANDLE)lpParam;
	DWORD bytesTransferred;
	LPOVERLAPPED lpOverlapped;
	LPPER_HANDLE_DATA perHandleData = NULL;
	LPPER_IO_DATA perIoData = NULL;
	DWORD sendBytes;
	DWORD recvBytes;
	DWORD flags;
	DWORD bRet;

	for (;;)
	{
		//等待完成端口通知
		bRet = GetQueuedCompletionStatus(completionPort, 
						 &bytesTransferred, 
						 (PULONG_PTR)&perHandleData, 
						 (LPOVERLAPPED *)&lpOverlapped, 
						 INFINITE);

		//根据CONTAINING_RECORD宏,计算类型指针地址
		//第一个参数是类型的成员的指针地址
		//第二个参数是类型
		//第三个参数是类型中成员名称
		perIoData = (LPPER_IO_DATA)CONTAINING_RECORD(lpOverlapped, PER_IO_DATA, overlappend);
		if (bytesTransferred == 0)
		{
			closesocket(perHandleData->socket);
			GlobalFree(perHandleData);
			GlobalFree(perIoData);
			continue;
		}

		//打印数据
		printf("%s\n", perIoData->dataBuf.buf);
		flags = 0;
		ZeroMemory(&(perIoData->overlappend), sizeof(OVERLAPPED));
		perIoData->dataBuf.len = 1024;
		perIoData->dataBuf.buf = perIoData->buffer;
		perIoData->OperationType = 0;
		WSARecv(perHandleData->socket,
			&(perIoData->dataBuf),
			1,
			&recvBytes,
			&flags,
			&(perIoData->overlappend),
			NULL);
	}
	return 0;
}
秋风 2017-04-23