【免杀前置课——Windows编程】十六、IOCP——输入输出完成端口( Input/Output Completion Port,IOCP) 、实现简易网络聊天室(多客户端 附代码)

IOCP

输入输出完成端口( Input/Output Completion Port,IOCP)

是支持多个同时发生的异步I/0操作的应用程序编程接口,IOCP特别适合C/S模式网络服务器端模型。因为,让每一个socket有一个线程负责同步(阻塞)数据处理,one-thread-per-client的缺点是:一是如果连入的客户多了,就需要同样多的线程;二是不同的socket 的数据处理都要线程切换的代价。
最佳线程数量=cpu内核数量2*
一个IOCP对象,在操作系统中可关联着多个Socket和(或)文件控制端。***IOCP对象内部有一个先进先出(FIFO)队列,用于存放IOCP所关联的输入输出端的服务请求完成消息。请求输入输出服务的进程不接收IO服务完成通知,而是检查IOCP的消息队列以确定I0请求的状态。(线程池中的>多个线程负责从IOCP 消息队列中取走完成通知并执行数据处理;如果队列中没有消息,那么线程阻塞挂起在该队列。***这些线程从而实现了负载均衡。
IOCP是一个内核对象,但是他是一个不需要安全属性的Windows内核对象。

在这里插入图片描述

实现简易网络聊天室

服务器端

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#include<vector>
using std::vector;

vector<SOCKET> clientList;//保存多个线程的客户端

typedef struct _MY_OVERLAPPED
{
	OVERLAPPED overLapped;
	WSABUF wsaBuf;
}_MY_OVERLAPPED, * LPMY_OVERLAPPED;

DWORD WINAPI revcMessage(LPVOID param)
{
	BOOL result=FALSE;
	DWORD lpNumberOfBytesTransferred=0;
	ULONG_PTR client = 0;
	LPMY_OVERLAPPED lpOverLapped = NULL;
	DWORD flag = 0;
	//从IOCP队列中取出数据
	while (true)
	{
		result = GetQueuedCompletionStatus((HANDLE)param, &lpNumberOfBytesTransferred, &client, (LPOVERLAPPED*) & lpOverLapped, INFINITE);
		if (lpNumberOfBytesTransferred>0&&result)//若当前客户端未断开连接
		{
			for (int i = 0; i < clientList.size(); i++)//遍历进程中的每一个客户端线程
			{
				if (client != clientList[i])//判断是不是当前线程发的消息,若不是则发送给每个客户端
				{
					send(clientList[i], lpOverLapped->wsaBuf.buf, lpOverLapped->wsaBuf.len, 0);
				}
			}
			//清空OVERLAPPED
			memset(&lpOverLapped->overLapped, 0, sizeof(OVERLAPPED));
			//清空缓冲区
			memset(lpOverLapped->wsaBuf.buf, 0, 0x100);
			WSARecv(client, &lpOverLapped->wsaBuf, 1, &lpNumberOfBytesTransferred, &flag,(LPWSAOVERLAPPED)lpOverLapped,NULL);
		}
		else
		{
			for (int i = 0; i < clientList.size(); i++)
			{
				if (clientList[i] == client)//判断哪一个客户端线程断开了连接
				{
					printf("客户端%u断开了连接\n", clientList[i]);
					closesocket(clientList[i]);
					clientList.erase(clientList.begin() + i);
				}
			}
		}
	}
	
	return 0;
}

int main()
{
	//创建IOCP对象
	HANDLE hiocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
	//获取CPU内核数量
	SYSTEM_INFO sysInfo{ 0 };
	GetNativeSystemInfo(&sysInfo);
	for (int i = 0; i < sysInfo.dwNumberOfProcessors*2; i++)//获取CPU内核数量*2是因为在CPU数量*2时是最佳线程数量
	{
		//创建线程实现并发通信
		CreateThread(NULL, NULL, revcMessage, (LPVOID)hiocp, NULL, NULL);
	}
	//1、初始化网络环境
	WSADATA data{ 0 };
	WSAStartup(MAKEWORD(2,2), &data);
	//2、创建Socket
	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//用IPv4地址协议簇,TCP连接
	//3、指定IP地址和端口号
	sockaddr_in serverAddr{ 0 };
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(7777);
	inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);//InetPton函数将标准文本表示形式的 IPv4 或 IPv6 互联网网络地址转换为数字二进制形式
	//printf("%s\n", serverAddr.sin_addr);
	bind(server, (SOCKADDR*)&serverAddr, sizeof(serverAddr));//绑定函数将本地地址与套接字相关联。
	//4、监听
	listen(server, SOMAXCONN);
	//5、接受会话
	sockaddr_in clientAddr{ 0 };
	int size = sizeof(clientAddr);
	while (1)
	{
		SOCKET client = accept(server, (SOCKADDR*)&clientAddr, &size);//绑定将监听地址与套接字
		clientList.push_back(client);
		//将SOCKET关联到IOCP
		CreateIoCompletionPort((HANDLE)client, hiocp, client, 0);
		LPMY_OVERLAPPED lpOverLapped = new _MY_OVERLAPPED{ 0 };
		lpOverLapped->wsaBuf.len = 0x100;
		lpOverLapped->wsaBuf.buf = new char[0X100];
		DWORD realRead = 0;
		DWORD flag = 0;
		//接受消息
		WSARecv(client, &lpOverLapped->wsaBuf, 1, &realRead,& flag,(LPWSAOVERLAPPED)lpOverLapped, NULL);
	}
	closesocket(server);
	return 0;
}

客户端

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<ws2tcpip.h>

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

DWORD WINAPI revcMessage(LPVOID param)
{
	SOCKET client = (SOCKET)param;
	char buff[0x100]{ 0 };
	while (recv(client, buff, 0x100, 0) > 0)
	{
		printf("%s\n", buff);

	}
	return 0;
}

int main()
{
	//1、初始化网络环境
	WSADATA data{ 0 };
	WSAStartup(MAKEWORD(2, 2), &data);
	//2、创建Socket
	SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//用IPv4地址协议簇,TCP连接
	//3、指定IP地址和端口号
	sockaddr_in serverAddr{ 0 };
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(7777);
	inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);//InetPton函数将标准文本表示形式的 IPv4 或 IPv6 互联网网络地址转换为数字二进制形式
	//4、连接
	int result = connect(client, (SOCKADDR*)&serverAddr, sizeof(serverAddr));
	if (result)
	{
		printf("连接服务器失败\n");
	}
	HANDLE hThread = CreateThread(NULL, NULL, revcMessage, (LPVOID)client, NULL, NULL);
	char buff[0x100]{ 0 };
	while (scanf_s("%s",buff,0x100) && strcmp(buff,"exit"))//接受用户输入
	{
		send(client, buff, strlen(buff) + 1, 0);
	}
	closesocket(client);
	CloseHandle(hThread);
	/*send(client, "我有话说\n", 8, 0);
	char buff[0x100]{ 0 };
	recv(client, buff, 0x100, 0);
	printf("server:%s", buff);
	system("pause");*/
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webfker from 0 to 1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值