网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事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)**