iocp模型

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

#pragma comment(lib, "Ws2_32.lib")      // Socket编程需用的动态链接库  
#pragma comment(lib, "Kernel32.lib")    // IOCP需要用到的动态链接库
//#pragma comment(lib,"mswsock.lib")	

#include <mswsock.h>//微软扩展库,后面获取AcceptEx指针用到


const int BufSize = 2 * 1024;
CRITICAL_SECTION cs;
//重叠I/O所用到的结构体

typedef struct Per_IO_Context
{
	OVERLAPPED overlapped;//每一个重叠I/O都需要有一个
	WSABUF dataBuf;//存储数据的缓冲区,用来给重叠操作传递参数
	char buffer[BufSize];//对应的WSABUF里的缓冲区
	int bufLength;//缓冲区长度
	int operationType;//标志这个重叠I/O是做什么的,Accept  Recv之类的
	SOCKET m_sockAccept;//这个I/O所用的socket,每个连接都是一样的
}PER_IO_CONTEXT;
typedef struct Per_Sock_Context
{
	SOCKET m_sock;//每一个客户端连接的socket
	SOCKADDR_IN m_addr;//这个客户端对应的地址
}PER_SOCK_CONTEXT;
vector<struct Per_Sock_Context*>clientGroup;//客户端数组
SOCKET server_socket;
int main()
{//	函数声明
	DWORD WINAPI ServerWorkThread(LPVOID lpa);
	DWORD WINAPI ServerSendThread(LPVOID lpa);
	DWORD WINAPI AcceptThread(LPVOID lpa);
	HANDLE ThreadHandle;
	InitializeCriticalSection(&cs);
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
	HANDLE completPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);//创建io内核对象
	if (completPort == NULL)
	{
		cerr << "创建IO内核对象失败!!错误代码: " << GetLastError() << endl;
		system("pause");
		return -1;
	}
	
	SYSTEM_INFO m_sysInfo;
	GetSystemInfo(&m_sysInfo);//获取cpu的核心数量
	for (DWORD i = 0; i < m_sysInfo.dwNumberOfProcessors * 2; i++)
	{	//创建服务器工作线程,并将完成端口传递到该线程
		ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completPort, 0, NULL);
		if (ThreadHandle == NULL)
		{
			cerr << "创建线程句柄失败!!错误码: " << GetLastError() << endl;
			system("pause");
			return -1;
		}
	}
	//建立流式套接字
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6000);
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	server_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	PER_SOCK_CONTEXT* perDandleData;
	perDandleData = (PER_SOCK_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_SOCK_CONTEXT));
	perDandleData->m_sock = server_socket;
	CreateIoCompletionPort((HANDLE)server_socket, completPort, (ULONG_PTR)perDandleData, 0);

	//绑定socket
	if (bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
	{
		cerr << "绑定地址失败!!\n错误码为: " << GetLastError() << endl;
		system("pause");
		return -1;
	}
	if (listen(server_socket, 10) == SOCKET_ERROR)
	{
		cerr << "监听失败!!\n错误码: " << GetLastError << endl;
		system("pause");
		return -1;
	}
	cout << "服务端已开启,正在等待客户端连接...\nIP :127.0.0.1\tPort :6000" << endl;
	HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);
	HANDLE acceptThread = CreateThread(NULL, 0, AcceptThread, completPort, 0, NULL);//连接线程中因为需要绑定完成端口,所以传递句柄
	
	WaitForSingleObject(sendThread, INFINITE);
	WaitForSingleObject(acceptThread, INFINITE);
	WaitForSingleObject(ThreadHandle, INFINITE);
	DeleteCriticalSection(&cs);
	DeleteObject(sendThread);
	DeleteObject(acceptThread);
	DeleteObject(ThreadHandle);
	system("pause");
	return 0;
}
DWORD WINAPI AcceptThread(LPVOID lpa)
{
	HANDLE completPort = (HANDLE)lpa;
	while (true)
	{
		PER_SOCK_CONTEXT *perHandleData = NULL;
		PER_IO_CONTEXT *perIOData = NULL;
		SOCKADDR_IN remoteAddr;
		SOCKET acceptSock;
		int len = sizeof(remoteAddr);

	/*	perIOData = (PER_IO_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_IO_CONTEXT));
		ZeroMemory(&(perIOData->overlapped), sizeof(OVERLAPPED));//置0

		LPFN_ACCEPTEX m_lpfnAcceptEx;         // AcceptEx函数指针   
		GUID GuidAcceptEx = WSAID_ACCEPTEX;        // GUID,这个是识别AcceptEx函数必须的   
		DWORD dwBytes = 0;
		if (0!=WSAIoctl(server_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx),//第一个参数只要是一个有效的socket就可以
			&m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx), &dwBytes, NULL, NULL))//获取AcceptEx指针
		{
			cerr << WSAGetLastError() << endl;
			return -1;
		}
		BOOL rc = m_lpfnAcceptEx(server_socket,
			perIOData->m_sockAccept,
			perIOData->buffer, 
			perIOData->bufLength - ((sizeof(SOCKADDR_IN) + 16) * 2),
			sizeof(SOCKADDR_IN) + 16,
			sizeof(SOCKADDR_IN) + 16,
			&dwBytes, 
			&(perIOData->overlapped)
			);
		if(rc == FALSE)
			break;
		cout << "连入一个客户端!" << endl;
		LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptExSockAddrs;  //同上的AcceptEx       
		GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;    
		WSAIoctl(perHandleData->m_sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs,
			sizeof(GuidGetAcceptExSockAddrs), &m_lpfnGetAcceptExSockAddrs,sizeof(m_lpfnGetAcceptExSockAddrs), &dwBytes,NULL,NULL);
		SOCKADDR_IN* ClientAddr = NULL;
		SOCKADDR_IN* LocalAddr = NULL;
		int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);
		m_lpfnGetAcceptExSockAddrs(perIOData->dataBuf.buf, perIOData->dataBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2), 
			sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);
			*/
		acceptSock = accept(server_socket, (struct sockaddr*)&remoteAddr, &len);
		if (acceptSock == SOCKET_ERROR)
		{
			cerr << "Accept Socket ERROR!!\nError Code: " << GetLastError() << endl;
			system("pause");
			return -1;
		}
		// 创建用来和套接字关联的单句柄数据信息结构
		perHandleData = (PER_SOCK_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_SOCK_CONTEXT));	// 在堆中申请指定大小的内存  
		perHandleData->m_sock =acceptSock;//将新连接的客户端的信息保存下来
		memcpy(&perHandleData->m_addr, &remoteAddr, len);
		clientGroup.push_back(perHandleData);
		//将套接字与完成端口关联
		CreateIoCompletionPort((HANDLE)(perHandleData->m_sock), completPort, (DWORD)perHandleData, 0);
		// 开始在接受套接字上处理I/O使用重叠I/O机制  
		// 在新建的套接字上投递一个或多个异步  
		// WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务      
		// 单I/O操作数据(I/O重叠) 
		PER_IO_CONTEXT * PerIOData = NULL;
		PerIOData = (PER_IO_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_IO_CONTEXT));
		ZeroMemory(&(PerIOData->overlapped), sizeof(OVERLAPPED));//置0
		PerIOData->dataBuf.len = 1024;
		PerIOData->dataBuf.buf = PerIOData->buffer;
		PerIOData->operationType = 0; //read

		DWORD RecvBytes;
		DWORD Flags = 0;
		//投递第一个I/O请求
		WSARecv(perHandleData->m_sock, &(PerIOData->dataBuf), 1, &RecvBytes, &Flags, &(PerIOData->overlapped), NULL);
	}
	return 0;
}
DWORD WINAPI ServerWorkThread(LPVOID lpa)
{
	HANDLE CompletionPort = (HANDLE)lpa;//传递的是上面创建的I/O内核对象
	LPOVERLAPPED lpoverlapped;
	PER_SOCK_CONTEXT* perHandleData = NULL;
	PER_IO_CONTEXT * perIOData = NULL;
	int result;
	DWORD BytesTransferred;
	DWORD Flags = 0;
	DWORD receiveBytes;
	while (true)
	{
		/*	GetQueuedCompletionStatus 参数含义:
		CompletionPort:指定的IOCP,该值由CreateIoCompletionPort函数创建。
		lpnumberofbytes:一次完成后的I/O操作所传送数据的字节数。
		lpcompletionkey:当文件I/O操作完成后,用于存放与之关联的CK。
		lpoverlapped:为调用IOCP机制所引用的OVERLAPPED结构。
		dwmilliseconds:用于指定调用者等待CP的时间。
		*/
		result = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&perHandleData,
			(LPOVERLAPPED*)&lpoverlapped, INFINITE);
		if (result == 0)
		{
			cerr << "GetQueuedCompletionStatus Error: " << GetLastError() << endl;
			continue;
		}
		//根据结构体中的某成员的地址来推算出该结构体整体的地址!
		perIOData = (PER_IO_CONTEXT*)CONTAINING_RECORD(lpoverlapped, PER_IO_CONTEXT, overlapped);
		if (BytesTransferred == 0)//如果有错误发生
		{
			closesocket(perHandleData->m_sock);//关闭socket
			GlobalFree(perHandleData);//释放内存
			GlobalFree(perIOData);
			continue;
		}
		// 开始数据处理,接收来自客户端的数据  
		EnterCriticalSection(&cs);
		cout << "Receive: " << perIOData->dataBuf.buf << endl;
		LeaveCriticalSection(&cs);

		// 为下一个重叠调用建立单I/O操作数据  
		ZeroMemory(&perIOData->overlapped, sizeof(OVERLAPPED));//清0
		perIOData->dataBuf.len = 1024;
		ZeroMemory(perIOData->dataBuf.buf, 1024);
		perIOData->operationType = 0;    // read  
		WSARecv(perHandleData->m_sock, &(perIOData->dataBuf), 1, &receiveBytes, &Flags, &(perIOData->overlapped), NULL);
	}


	return 0;
}
DWORD WINAPI ServerSendThread(LPVOID lpa)
{
	while (TRUE)
	{
		char talk[200];
		cin >> talk;
		int i = 0;
		while (talk[i] != '\0')
			i++;
		talk[i] = '\n';
		talk[++i] = '\0';
		cout << "Send: " << talk << endl;
		EnterCriticalSection(&cs);//互斥访问连接的socket数组
		for (int i = 0; i < clientGroup.size(); ++i) {
			send(clientGroup[i]->m_sock, talk, 200, 0);  // 发送信息  
		}
		LeaveCriticalSection(&cs);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值