2024年C C++最全Win-服务器端 IOCP模型_one-thread-per-client,2024年最新25岁成功入职阿里P7的小哥哥告诉你

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

等待线程队列(WaitingThreadList-LIFO)
释放线程队列(ReleasedThreadList)
暂停线程队列(PausedThreadList)

4、完成端口实现流程
1、创建一个完成端口CreateIoCompletionPort()
2、根据OS中CPU核心数量建立对应Worker线程
3、创建一个用于监听socket,绑定到完成端口上,然后开始在指定的端口上监听客户端连接请求
4、必须在监听socket上投递AcceptEx请求(mswsock.dIl mswsock.lib等)
5、Worker线程干的事情:使用GetQueuedCompletionStatus()监控完成端口
6、当接收到AcceptEx通知时 _DoAccept0
7、当收到Recv通知时,_DoRecv0
8、关闭完成端口

5、在这里插入图片描述

6、IOCPServer.cpp

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

// IOCP需要调用到动态链接库
#pragma comment(lib,"Kernel32.lib")
// Socket编程需要用的动态连接库
#pragma comment(lib,"Ws2\_32.lib")

const int g_DataBufferSize = 2048;			// 服务器端口
typedef struct{
	OVERLAPPED overLapped;
	WSABUF dataBuffer;
	char buffer[g_DataBufferSize];
	int bufferLength;
	int operationType;
}PER_IO_OPERATEION_DATA,\*LPPER_IO_OPERATION_DATA,\*LPPER_IO_DATA,PER_IO_DATA;

typedef struct{
	SOCKET socket;
	SOCKADDR_STORAGE clientAddr;
	char \*pszClientName;
}PER_HANDLE_DATA,\*LPPER_HANDLE_DATA;

// 设置自定义全局变量
HANDLE ComletionPort = NULL;
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

const int g_DefaultPort = 6688;
SOCKET serverSocket;			// 服务器socket
vector<PER_HANDLE_DATA\*> g_clientGroup;		// 专用于记录客户端向量组

// 设置自定义函数声明
BOOL InitNetwork(WORD port);	// 初始化网络相关操作
BOOL InitWorkThread();		// 初始化工作者线程
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);		// 工作者线程处理函数
DWORD WINAPI ServerSendThread(LPVOID lpParam);	// 发送消息的函数
DWORD WINAPI AddClient(LPVOID lpParam);		// 向服务器添加客户端
BOOL DeleteClient(vector < PER_HANDLE_DATA\*>&g_clientGroup, SOCKET&clientSocket);		// 向服务器删除客户端(退出)
int myStringLen(const char \*str);	// 字符串处理函数


int \_tmain(int argc, _TCHAR\* argv[])
{
	if (!InitNetwork(g_DefaultPort))
		return -1;

	if (!InitWorkThread())  // 错误点
		return -1;
	HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);
	HANDLE addThread = CreateThread(NULL, 0, AddClient, 0, 0, NULL);
	WaitForSingleObject(sendThread, INFINITE);
	WaitForSingleObject(addThread, INFINITE);

	system("pause");

	return 0;
}

int myStringLen(const char \*str)
{
	int i = 0;
	while (\*str != '\0')
	{
		i++;
		str++;
	}
	return i;
}

BOOL InitNetwork(WORD port)	// 初始化网络相关操作
{
	WORD wVersionRequested = MAKEWORD(2, 2);
	WSADATA wsaData;
	DWORD Error = WSAStartup(wVersionRequested, &wsaData);
	
	if (Error != 0)
	{
		printf("\n初始化请求动态连接库失败.\n\n");
		return FALSE;
	}

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		cerr << "\n请求的版本不是2.2版本.\n\n";
		return FALSE;
	}

	// 建立服务器socket
	serverSocket = socket(AF_INET, SOCK_STREAM, 0);	// TCP协议流式套接字
	SOCKADDR_IN serverAddress;
	serverAddress.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_port = htons(port);

	int bindResult = bind(serverSocket, (SOCKADDR\*)&serverAddress, sizeof(SOCKADDR));
	if (bindResult == SOCKET_ERROR)
	{
		cerr << "\n绑定服务器失败:" << GetLastError() << endl;
		return FALSE;
	}

	// 将SOCKET套接字设置为监听模式
	int listenResult = listen(serverSocket, 100);
	if (SOCKET_ERROR == listenResult)
	{
		cerr << "\n服务器进入监听模式失败:" << GetLastError() << endl;
		return FALSE;
	}

	cout << "\n\n\tIOCP模型服务器端已准备就绪,正在等待客户端连接..........\n" << endl;

	return TRUE;
}

BOOL InitWorkThread()		// 初始化工作者线程
{
	// 首先创建IOCP内核对象
	HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (NULL == completionPort)	// 判断创建IO内核对象是否成功,失败就执行如下语句块
	{
		cerr << "创建IO完成端口失败:" << GetLastError() << endl;
		return FALSE;
	}

	// 创建IOCP线程(线程里面创建线程池,并且将完成端口传递到内核该线程) 内核:单核 双核 微内核
	SYSTEM_INFO mySystemInfo;
	GetSystemInfo(&mySystemInfo); // 确定处理器的核心数量
	
	// 根据CPU的核心数量创建对应线程
	for (DWORD i = 0; i < (mySystemInfo.dwNumberOfProcessors \* 2 + 2); i++)
	{
		HANDLE threadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);
		if (NULL == threadHandle)
		{
			cerr << "创建线程失败:" << GetLastError() << endl;
			return FALSE;
		}
		CloseHandle(threadHandle);
	}

	return TRUE;
}

DWORD WINAPI ServerWorkThread(LPVOID lpParam) //工作者线程处理函数
{
	ComletionPort = (HANDLE)lpParam;
	DWORD BytesTransferred;
	LPOVERLAPPED IpOverlapped;
	LPPER_HANDLE_DATA PerHanleData = NULL;
	LPPER_IO_DATA PerIoData = NULL;
	DWORD RecvBytes;
	DWORD Flags = 0;
	BOOL bRet = false;

	while (true)
	{
		bRet = GetQueuedCompletionStatus(ComletionPort, &BytesTransferred, (PULONG_PTR)&PerHanleData, (LPOVERLAPPED\*)&IpOverlapped, INFINITE);
		if (bRet == 0)
		{
			cerr << "\n获取完成端口状态发生失败:" << GetLastError() << endl;
			DeleteClient(g_clientGroup, PerHanleData->socket);
			continue;
		}


		PerIoData = (LPPER_IO_DATA)CONTAINING\_RECORD(IpOverlapped, PER_IO_DATA, overLapped);

		// 检查在套接字上是否有错误发生
		if (BytesTransferred == 0)
		{
			closesocket(PerHanleData->socket);
			GlobalFree(PerHanleData);
			GlobalFree(PerIoData);
			continue;
		}

		// 开始处理数据信息,接收来自客户端的数据
		WaitForSingleObject(hMutex, INFINITE);
		SOCKET clientsock = NULL;
		cout << "有客户端数据为:" << PerIoData->dataBuffer.buf << endl;
		ReleaseMutex(hMutex);

		// 为下一个重叠调用建立单个IO操作数据
		ZeroMemory(&(PerIoData->overLapped), sizeof(OVERLAPPED));
		PerIoData->dataBuffer.len = 1024;
		PerIoData->dataBuffer.buf = PerIoData->buffer;
		PerIoData->operationType = 0;		// 读操作
		WSARecv(PerHanleData->socket, &(PerIoData->dataBuffer), 1, &RecvBytes, &Flags, &(PerIoData->overLapped), NULL);
	}

	return TRUE;
}

BOOL DeleteClient(vector < PER_HANDLE_DATA\*>&g_clientGroup, SOCKET&clientSocket)	// 向服务器删除客户端(退出)
{
	for (int i = 0; i < g_clientGroup.size(); i++)
	{
		if (g_clientGroup[i]->socket == clientSocket)
		{
			g_clientGroup.erase(g_clientGroup.begin() + i);
			cout << "已监测到客户端退出.\n\n";
			return TRUE;
		}
	}

	return FALSE;
}

DWORD WINAPI ServerSendThread(LPVOID lpParam)	// 发送消息的函数
{
	while (1)
	{
		char data[2048];
		gets\_s(data);
		int len;
		for (len = 0; data[len] != '\0'; len++);
		
		data[len] = '\n';
		data[++len] = '\0';
		cout << "数据为:" << data << g_clientGroup.size() << endl;

		WaitForSingleObject(hMutex, INFINITE);
		for (int i = 0; i < g_clientGroup.size(); ++i)
		{
			//发送数据
			send(g_clientGroup[i]->socket, data, 2048, 0);
		}
		ReleaseMutex(hMutex);
	}

	return 0;
}

DWORD WINAPI AddClient(LPVOID lpParam)		// 向服务器添加客户端
{
	while (true)
	{
		PER_HANDLE_DATA \*PerHandleData = NULL;
		SOCKADDR_IN saRemote;
		int RemoteLen;
		SOCKET acceptSocket;

		RemoteLen = sizeof(saRemote);
		acceptSocket = accept(serverSocket, (SOCKADDR\*)&saRemote, &RemoteLen);
		if (acceptSocket == SOCKET_ERROR)
		{
			cerr << "接收客户端失败:" << GetLastError() << endl;
			return -1;


![img](https://img-blog.csdnimg.cn/img_convert/9176e57ac74ab08d16e7b3f9b5753ca7.png)
![img](https://img-blog.csdnimg.cn/img_convert/f9e75efcdf3632603d52df1240b9b92e.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

[外链图片转存中...(img-PsYI68lo-1715531471135)]
[外链图片转存中...(img-AYFFFqRw-1715531471136)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值