iocp简单例子
下方代码中,没有写注释的地方,说明与icop网络无关也就是它们都不重要,重要的位置全部都有注释,复制下方代码就可以运行看效果
iocp带网络的例子:
客户端:
客户端只有一个main,只有socket相关函数,无iocp
客户端说明:
由于和客户端只用了socket相关函数,所以客户端不重要,重要的是服务端,它使用iocp网络实现,所以用手机开热点让电脑连接热点,然后用手机浏览器访问电脑的ip和服务端开启的端口就可以看iocp网络(iocp网络的意思是,socket 加 iocp配合,实现的网络数据的接收与发送),或者用路由器让手机和电脑在一个网络下也可以,所以如果只关心iocp网络,下方客户端的代码不需要看,用手机调试 或者 用电脑浏览器调试
客户端代码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS #pragma comment(lib, "Ws2_32.lib") #include <winsock2.h> #include <string> #include <stdio.h> #include <stdlib.h>class COverlapped { public:/**重叠结构 就是 OVERLAPPED(它的中文意思是重叠,所以称它是重叠结构)*/OVERLAPPED m_overlapped;DWORD m_operator;char m_buffer[4096];SOCKET client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);COverlapped() {m_operator = 0;memset(&m_overlapped, 0, sizeof(m_overlapped));memset(m_buffer, 0, sizeof(m_buffer));} };int main(int argc, char* argv[]) {WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){printf("WSAStartup() error");return -1;}SOCKET hSocket;hSocket = socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN servAddr{};servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");servAddr.sin_port = htons(9527);/*********************************************连接*********************************************************/if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR){printf("connect error");return -1;}/*********************************************连接完成*********************************************************//*********************************************等待接收服务端发来的数据*********************************************************/char message[30];int strlen = recv(hSocket, message, 30, 0);if (strlen == -1){printf("recv error");return -1;}printf("recv message: %s\n", message);/*********************************************等待接收服务端发来的数据结束*********************************************************//*********************************************给服务端发数据*********************************************************//*下方代码(char* str = XXXX这个代码)是写着玩的,现在没有任何作用,所以给注释了char* str = (char*)"fkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffkhsklffk";*/for (int i = 4; i < 100; i++){;/*send函数发送一个数据给socketstd::to_string(i).c_str() 把一个数字转成字符串*/send(hSocket, std::to_string(i).c_str(), ::strlen(std::to_string(i).c_str())+1, 0);printf("%s\n", std::to_string(i).c_str());// Sleep(1000);// WSASend函数发送数据,这里的WSASend没有绑定iocp(这个代码无iocp,单纯的socket),所以它只能发送,发送成功还是发送不成功,没有iocp没法知道结果/* WSABUF m_wsabuffer{};DWORD m_received;DWORD m_flags;m_flags = 0;m_received = 0;COverlapped overlapped2{};overlapped2.m_operator = 2;m_wsabuffer.buf = (char*)std::to_string(i).c_str();m_wsabuffer.len = ::strlen(std::to_string(i).c_str())+1;WSASend(hSocket, &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);int tt = WSAGetLastError();int a = tt;*/}// 下方是用 WSASend函数 发送一个指定的字符串给服务器/*WSABUF m_wsabuffer{};DWORD m_received;DWORD m_flags;m_flags = 0;m_received = 0;COverlapped overlapped2{};overlapped2.m_operator = 2;char* str = (char*)"fkhsklf";m_wsabuffer.buf = str;m_wsabuffer.len = ::strlen(str) + 1;WSASend(hSocket, &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);*//*********************************************给服务端发数据结束*********************************************************/closesocket(hSocket);WSACleanup();system("pause");return 0; }
服务端:
服务端代码观看方式(观看思路):
从main函数开始看,或者搜索 WSARecv、AcceptEx、WSASend、CreateIoCompletionPort、GetQueuedCompletionStatus这些关键字
iocp网络相关函数说明:
WSARecv是注册一个接收数据的iocp,当接收到数据会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态,可以从第二个参数的内存地址得到接收的数据
AcceptEx是注册一个新连接的iocp,当新的连接连接成功后会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态
WSASend是注册一个发送数据的iocp,当发送成功会进入iocp回调函数,也就是 GetQueuedCompletionStatus退出阻塞状态
CreateIoCompletionPort让一个socket绑定一个iocp
GetQueuedCompletionStatus获取一个iocp,如果没有iocp,将进入阻塞
iocp:iocp(完成端口)官方说明
大概意思就是当一个进程创建了iocp之后,操作系统会给创建一个队列,让这个队列与进程进行关联,专门给这个进程处理请求服务,由于它是队列所以支持线程安全,可以把iocp当作一个高性能锁,它比任意多线程安全机制都要快
服务端代码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS #pragma comment(lib,"Ws2_32.lib") #pragma comment(lib,"Mswsock.lib") #include <process.h> #include <winsock2.h> #include <ws2tcpip.h> #include <mswsock.h> #include <stdio.h>int i = 0; // 这个不要管,写着玩的 char strarr[999999]{};// 接收数据的缓存,也就是 WSARecv 接收的数据class COverlapped { // 重叠结构 public:/**只要有 OVERLAPPED 这个类型并且放在第一个位置,那这个结构体就可以成为重叠结构*/OVERLAPPED m_overlapped;DWORD m_operator;int clientIndex;char m_buffer[4096];COverlapped() {m_operator = 0;memset(&m_overlapped, 0, sizeof(m_overlapped));memset(m_buffer, 0, sizeof(m_buffer));} }; HANDLE hIOCP; SOCKET sock;/*最多只能连接5个客户端,如果要更多的客户端,需要添加心跳包功能,用心跳包检测socket连接状态,也就是服务端给客户端发送一个数据,客户端接收到必须马上给服务端返回一个数据否则视为当前sokcet已经断开连接需要释放资源,这样释放资源之后就可以给新的客户端用了,也就可以处理新的连接了 */ SOCKET clients[5]; int i2 = 1; // socket的索引,也就是 clients 它的索引void iocp() {// SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); // TCPWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("WSAStartup failed.\n");return;}for (int i = 0; i < 5; i++){clients[i] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);}// 创建一个套接字用于绑定iocp,让AcceptEx生效sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);//client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);if (sock == INVALID_SOCKET) {int ttt = WSAGetLastError();int a = ttt;return;}// 重建一个iocphIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, sock, 4);// SOCKET client = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);// sock与iocp绑定CreateIoCompletionPort((HANDLE)sock, hIOCP, 0, 0);// 下方是设置套接字ip与端口sockaddr_in addr;addr.sin_family = PF_INET;// PF_INET 与 AF_INET是一个东西,用哪一个都行,混着用也行addr.sin_addr.s_addr = inet_addr("0.0.0.0");// 监听所有ipaddr.sin_port = htons(9527);bind(sock, (sockaddr*)&addr, sizeof(addr)); // ip信息绑定socketlisten(sock, 5);COverlapped _Aoverlapped; {}; // 创建重叠结构_Aoverlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用_Aoverlapped.clientIndex = 0;DWORD received = 0; // 固定写死// AcceptEx是异步的,调用之后很快就会返回,投递给iocp一个新连接事件if (AcceptEx(sock, clients[_Aoverlapped.clientIndex], _Aoverlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &_Aoverlapped.m_overlapped) == FALSE) {/*WSAGetLastError 获取上一个函数执行结果,只能针对套接字相关使用,它的返回值是上一个函数的执行状态,可以得到执行成功还是执行失败它有许多错误类型,可以去 msdn(微软官方文档简称msdn) 搜 WSAGetLastError,然后从 WSAGetLastError函数说明的页面里可以找到返回值都有哪些以及返回值说明*/if (WSAGetLastError() != WSA_IO_PENDING){return;}} }unsigned int __stdcall ThreadFunc(void * a) {// 开启线程while (true) // 代表一个线程{LPOVERLAPPED pOverlapped{};DWORD transferred = 0;DWORD key = 0;/*第二个参数,也就是transferred可以得到有效字节数,也就是 WSARecv 接收数据的长度依据tcp或udp协议不同,数据大会造成分包发送,分包是顺序的,不用担心数据会乱,所以需要在发送的数据包里,设置数据包大小然后通过 transferred 判断数据包是否接收完毕,比如 transferred 它的数字小于,我们数据包里设置的大小,就说明数据包没有接收完毕,需要拼接数据pOverlapped 获取重叠结构,重叠结构是用来在使用 iocp 时进行iocp内部的传参hIOCP 绑定了什么socket,它就触发什么,如果socket没有与它绑定,任何操作都不会触发 GetQueuedCompletionStatus 函数,也就是无法让 GetQueuedCompletionStatus 函数退出阻塞状态*/if (GetQueuedCompletionStatus(hIOCP, &transferred, &key, &pOverlapped, WSA_INFINITE)) {COverlapped* pO = CONTAINING_RECORD(pOverlapped, COverlapped, m_overlapped);switch (pO->m_operator){case 1: {printf("AcceptEx\n");/*有客户端连接,这里把客户端连接绑定到 IOCP 里,如果不绑定,WSARecv会失效,WSASend可以发送但是发送成功无法触发 GetQueuedCompletionStatus 函数的回调,也就是无法让 GetQueuedCompletionStatus 函数退出阻塞状态*/CreateIoCompletionPort((HANDLE)clients[pO->clientIndex], hIOCP, 0, 0);/*********************************************************************************************************************************************/DWORD Flags = 0; // 固定写死DWORD dwRecv = 0; // 固定写死COverlapped overlapped3{}; // 创建重叠结构,并初始化内存overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped3.clientIndex = pO->clientIndex;WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大/* 添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理*/WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);/*********************************************************************************************************************************************//*0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000*/sockaddr* pLocal = NULL, * pRemote = NULL;int nLocal = 0, nRemote = 0;/* GetAcceptExSockaddrs函数它的第一个参数是 AcceptEx 函数的第三个参数,AcceptEx函数会把本地ip和对方ip返回到第三个参数里GetAcceptExSockaddrs函数它只是用来解析远程地址和本地地址的*/GetAcceptExSockaddrs(&pO->m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &pLocal, &nLocal, &pRemote, &nRemote);sockaddr_in* pP = (sockaddr_in*)pLocal;printf("本地IP:%s ", inet_ntoa(pP->sin_addr));printf("对方端口号:%d \n", ntohs(pP->sin_port));sockaddr_in* pR = (sockaddr_in*)pRemote;printf("对方IP:%s ", inet_ntoa(pR->sin_addr));printf("对方端口号:%d \n", ntohs(pR->sin_port));/*0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000*//*============================================================================================================================================*/WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容DWORD m_received = 0; // 固定写死DWORD m_flags = 0; // 固定写死DWORD received = 0; // 固定写死COverlapped overlapped2{}; // 创建重叠结构overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped2.clientIndex = pO->clientIndex;char* str = (char*)"000";m_wsabuffer.buf = str; // 发送的数据内容m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1// 发送数据WSASend(clients[overlapped3.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);/*============================================================================================================================================*//*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/COverlapped overlapped{}; // 创建重叠结构overlapped.m_operator = 1; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped.clientIndex = i2++;/*当前是在 case 1 里,也就是当有了新的连接,它就会调用 GetQueuedCompletionStatus 函数的回调,然后再得到这是什么类型,如果类型是1就会进入 case 1 里,也就是会来段现在的位置下方的写法,首先AcceptEx是注册一个新连接事件(准确说是投递到iocp的队列里一个连接请求),也就是告诉iocp,当有了新连接就通知 GetQueuedCompletionStatus 函数的回调通知完AcceptEx注册了事件将被删除,被删除了,就说明下次没法触发了,所以这里再次注册 AcceptEx*/AcceptEx(sock, clients[overlapped.clientIndex], overlapped.m_buffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &overlapped.m_overlapped);/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/i++;break;}case 2: { // 这里是 WSASend 成功的处理//printf("send %d--\n", i);printf("send %d -- %s\n", i, strarr);WSABUF m_wsabuffer{}; // 缓冲区,发送的数据内容DWORD m_received = 0; // 固定写死DWORD m_flags = 0; // 固定写死DWORD received = 0; // 固定写死COverlapped overlapped2{}; // 创建重叠结构overlapped2.m_operator = 2; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped2.clientIndex = pO->clientIndex;char* str = (char*)"000";m_wsabuffer.buf = str; // 发送的数据内容m_wsabuffer.len = strlen(str) + 1; // 数据大小,C++里的字符串是以0结尾,strlen(str)计算的长度不包含0,所以这里+1// 发送数据// WSASend(clients[overlapped2.clientIndex], &m_wsabuffer, 1, &m_received, m_flags, &overlapped2.m_overlapped, NULL);break;}case 3: { // 这里是 WSARecv 成功的处理printf("recv %d -- %s\n", i, strarr);// 客户端循环发送数据,这里 strarr 里的值只是当前接收的数据,接收过的数据会被覆盖,所以要创建一个缓冲区,做数据拼接,在这一行下断点就可以看出这个问题DWORD Flags = 0; // 固定写死DWORD dwRecv = 0; // 固定写死COverlapped overlapped3{}; // 创建重叠结构,并初始化内存overlapped3.m_operator = 3; // 设置当前操作类型,用于在 GetQueuedCompletionStatus 函数的回调中使用overlapped3.clientIndex = pO->clientIndex;WSABUF m_wsabuffer2{}; // 接收数据的缓冲区结构m_wsabuffer2.buf = strarr; // 设置缓冲区,也就是一块存放数据的内存,当Recv成功之后,会把数据写到这里m_wsabuffer2.len = 999999; // 内存大小,buf只是一块内存,只有内存地址没有大小,所以这里告知程序此处内存有多大/*添加Recv监听,告诉iocp只要有Recv操作,也就是有人给我发了消息你就触发 GetQueuedCompletionStatus 函数的回调然后通过 m_operator 就能知道这是什么操作,然后在对应的switch里处理*/WSARecv(clients[overlapped3.clientIndex], &m_wsabuffer2, 1, &dwRecv, &Flags, &overlapped3.m_overlapped, NULL);break;}default:break;}}}return 0; } bool 控制台事件处理(DWORD type) {switch (type){case CTRL_CLOSE_EVENT: { // 点击控制台关闭时,会来到这里for (int i = 0; i < clientLen; i++){closesocket(clients[i]);clients[i] = INVALID_SOCKET;}closesocket(sock);CloseHandle(hIOCP);WSACleanup();printf("关闭套接字完成");return false;}default:break;} }int main() {// 给控制台添加一个监听,为了实现控制台关闭时关闭socket、iocp相关句柄资源if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)控制台事件处理, true))return 0;// 初始化 iocp 环境iocp();// iocp处理事件线程,主要看 ThreadFunc函数 里的case 1里的代码,看懂了它,就懂iocp网络了_beginthreadex(NULL, 0, ThreadFunc, (LPVOID)hIOCP, 0, NULL);/*=================================下方支持控制台输入同时避免主线程结束导致iocp网络结束=================================*/HANDLE ConsoleWin;INPUT_RECORD eventMsg;DWORD Pointer;ConsoleWin = GetStdHandle(STD_INPUT_HANDLE); // 获取控制台句柄,没有句柄没法获取它的输入while (1) { // ReadConsoleInput(ConsoleWin, &eventMsg, 1, &Pointer);//Read input msgif (eventMsg.EventType == MOUSE_EVENT && eventMsg.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) {printf("Right button clicked.");}if (eventMsg.EventType == MOUSE_EVENT && eventMsg.Event.MouseEvent.dwEventFlags == RIGHTMOST_BUTTON_PRESSED) {printf("Left button double clicked.");}if (eventMsg.EventType == KEY_EVENT) {/* eventMsg.Event.KeyEvent.uChar.AsciiChar只能获取键盘左边英文字母、数字、标点符号其它的要根据键盘的虚拟键码来得到(eventMsg.Event.KeyEvent.wVirtualKeyCode)*/printf("uChar:%c\t", eventMsg.Event.KeyEvent.uChar.AsciiChar);/*eventMsg.Event.KeyEvent.dwControlKeyState是用来控制键的状态也就是Ctrl、Alt、大写键的状态下方是 eventMsg.Event.KeyEvent.dwControlKeyState 的使用方式*/ int daxie = eventMsg.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON;int zCtrl = eventMsg.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED ;int nu = eventMsg.Event.KeyEvent.dwControlKeyState & NUMLOCK_ON;if (nu == NUMLOCK_ON) {printf("按下了nu\t");}if (daxie == CAPSLOCK_ON) {printf("开启大写\t");}if (zCtrl == LEFT_CTRL_PRESSED) {printf("按下了做Ctrl键\t");}printf("%x\t", eventMsg.Event.KeyEvent.dwControlKeyState);// 虚拟键码printf("当前:%X", eventMsg.Event.KeyEvent.wVirtualKeyCode);printf("\n");}}printf("\n退出\n");return 0; }
首先说明:纯iocp使用的例子看:纯iocp例子(里面的代码可能无法运行,但是下面的代码一定可以运行,可以看看它里面的 PostQueuedCompletionStatus函数 的使用,参考参考然后拿出来放到下面的代码里测试,搞几下就能懂了),主要涉及api:PostQueuedCompletionStatus(它可以触发一次iocp回调,也就是可以手动触发一次iocp回调,可以用来做多线程环境的锁)
控制台界面:控制台版坦克大战
相关文章:

iocp简单例子
下方代码中,没有写注释的地方,说明与icop网络无关也就是它们都不重要,重要的位置全部都有注释,复制下方代码就可以运行看效果 iocp带网络的例子: 客户端: 客户端只有一个main,只有socket相关函…...

HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例
HAL STM32 HW I2C DMA SSD1306/SH1106驱动示例 📍硬件I2C DMA驱动参考:https://blog.csdn.net/weixin_45065888/article/details/118225993 🔖本工程基于STM32F103VCT6,驱动程序独立,可以移植到任意STM32型号上使用。…...

grafana配置钉钉告警模版(一)
1、配置钉钉告警模版 创建钉钉告警模版,然后在创建钉钉告警时调用模版。 定义发送内容具体代码 my_text_alert_list 是模版名称后面再配置钉钉告警时需要调用。 {{/* 定义消息体片段 */}} {{ define "my_text_alert_list" }}{{ range . }}告警名称&…...

佳能2580的下载手册
凡是和电子产品有关的产品其内部都开始不断地进行内卷,在不断地内卷背后,意味着科技更新和换代,自己也入手了一台佳能2580的打印机,一台相对比较老式的打印机,以此不断地自己想要进行打印的需要。 下载的基础步骤&…...

YOLO-World:实时开放词汇目标检测
paper:https://arxiv.org/pdf/2401.17270.pdf Github:GitHub - AILab-CVC/YOLO-World: Real-Time Open-Vocabulary Object Detection online demo:https://huggingface.co/spaces/stevengrove/YOLO-World 目录 0. 摘要 1. 引言 2. 相关工…...

Unity中关于群组的一些组件
前言 在游戏开发环境中,UI组件是构建玩家交互界面的基础。以下是一些常见UI组件的详细解释和它们适用的场景,方便我们更好地理解和使用这些工具。 1. Graphic Raycaster Graphic Raycaster组件是游戏UI交互的核心。在Unity等游戏引擎中,当玩…...

面向对象详解,面向对象的三大特征:封装、继承、多态
文章目录 一、面向对象与面向过程1、什么是面向过程?2、什么是面向对象? 二、类与对象1. 初识对象2. 类的成员方法2.1 类的定义和使用2.2 成员方法 3. 类和对象4. 魔法方法1. _ _ inint _ _ 构造方法2. _ _ str _ _ 字符串方法3. _ _ lt _ _ 小于符号比较…...

【阿里云服务器的一些使用坑】都是无知的泪水呀
发生了什么? 我想学习一下关于Java的MySQL、Nginx 相关的知识。然后就用首次优惠注册的阿里云,都没有搞清楚实例,镜像,带宽,磁盘。然后。因为一不小心——我想去换一个Ubuntu的镜像而不是CentOS。就把实例给释放啊。之…...

Docker的常用命令||Docker是个流行的容器化平台,它允许你打包、分发和运行应用程序。
Docker是一个流行的容器化平台,它允许你打包、分发和运行应用程序。以下是一些常用的Docker命令及其示例用法: 1. **docker run**: 用于运行一个新的容器实例。 docker run <image_name> 例如,运行一个Nginx容器: docker ru…...

汽车电子论文学习--电动汽车电机驱动系统动力学特性分析
关键重点: 1. 汽车的低速转矩存在最大限制,受附着力限制,因路面不同而变化。 2. 起步加速至规定转速的时间可以计算得到: 3. 电机额定功率的计算方式: 可以采取最高设计车速90%或120km/h匀速行驶的功率作为电机额定功…...

c++的一些陌生用法记录
c的一些陌生用法记录 1. 完美转发std::forward<decltype(PH1)>(PH1)static的用法 1. 完美转发std::forward<decltype(PH1)>(PH1) static的用法 static函数与普通函数的区别: 用static修饰的函数,本限定在本源码文件中,不能被本源…...

Vue | (三)使用Vue脚手架(中)| 尚硅谷Vue2.0+Vue3.0全套教程
文章目录 📚Todo-list 案例🐇组件化编码流程(通用)🐇实现静态组件🐇展示动态数据🐇交互⭐️添加一个todo⭐️todo勾选实现⭐️删除功能实现⭐️底部统计功能实现⭐️底部全选功能实现⭐️底部一…...

TenorFlow多层感知机识别手写体
文章目录 数据准备建立模型建立输入层 x建立隐藏层h1建立隐藏层h2建立输出层 定义训练方式建立训练数据label真实值 placeholder定义loss function选择optimizer 定义评估模型的准确率计算每一项数据是否正确预测将计算预测正确结果,加总平均 开始训练画出误差执行结…...

Java基础(二十六):Java8 Stream流及Optional类
Java基础系列文章 Java基础(一):语言概述 Java基础(二):原码、反码、补码及进制之间的运算 Java基础(三):数据类型与进制 Java基础(四):逻辑运算符和位运算符 Java基础(五):流程控制语句 Java基础(六)࿱…...

qt - 19种精美软件样式
qt - 19种精美软件样式 一、效果演示二、核心程序三、下载链接 一、效果演示 二、核心程序 #include "mainwindow.h"#include <QtAdvancedStylesheet.h> #include <QmlStyleUrlInterceptor.h>#include "ui_mainwindow.h" #include <QDir&g…...

vue 使用docx库生成word表格文档
在Vue.js中生成Word表格文档,可以通过前端库来实现。这些库可以帮助我们轻松地将HTML表格转换为Word文档(通常是.docx格式)。以下是一些流行的前端库,它们可以用于在Vue项目中生成Word表格文档: docx…...

ElementUI table表格组件实现双击编辑单元格失去焦点还原,支持多单元格
在使用ElementUI table表格组件时有时需要双击单元格显示编辑状态,失去焦点时还原表格显示。 实现思路: 在数据中增加isFocus:false.控制是否显示在table中用cell-dblclick双击方法 先看效果: 上源码:在表格模板中用scope.row…...

Java基于SpringBoot+Vue的图书管理系统
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...

【云安全】Hypervisor与虚拟机
Hypervisor 也被称为虚拟机监视器(Virtual Machine Monitor,VMM),主要作用是让多个操作系统可以在同一台物理机上运行。 Type-1 Hypervisor 与 Typer-2 Hypervisor Type-1 Hypervisor 直接安装在物理服务器上,不依赖…...

JS文本加密方法探究
在前端开发中,有时候我们需要对敏感文本进行简单的加密,以提高安全性。本文将介绍一种基于 JavaScript 实现的文本加密方法,使用了 Base64、Unicode 和 ROT13 编码。 示例代码 function encodeText(text) {// Base64编码var base64Encoded …...

推荐彩虹知识付费商城免授权7.0源码
彩虹知识付费商城免授权7.0源码,最低配置环境 PHP7.2 1、上传源码到网站根目录,导入数据库文件:xydai.sql 2、修改数据库配置文件:/config.php 3、后台:/admin 账号:admin 密码:123456 4、前…...

【天衍系列 04】深入理解Flink的ElasticsearchSink组件:实时数据流如何无缝地流向Elasticsearch
文章目录 01 Elasticsearch Sink 基础概念02 Elasticsearch Sink 工作原理03 Elasticsearch Sink 核心组件04 Elasticsearch Sink 配置参数05 Elasticsearch Sink 依赖管理06 Elasticsearch Sink 初阶实战07 Elasticsearch Sink 进阶实战7.1 包结构 & 项目配置项目配置appl…...

一、ActiveMQ介绍
ActiveMQ介绍 一、JMS1.jms介绍2.jms消息传递模式3.JMS编码总体架构 二、消息中间件三、ActiveMQ介绍1.引入的原因1.1 原因1.2 遇到的问题1.3 解决思路 2.定义3.特点3.1 异步处理3.2 应用系统之间解耦3.3 实际-整体架构 4.作用 一、JMS 1.jms介绍 jms是java消息服务接口规范&…...

【牛客】寒假训练营1 I-It‘s bertrand paradox. Again! 题解
传送门:It’s bertrand paradox. Again! 标签:随机 题目大意 有两个人分别用两种方式在二维平面上随机生成1e5个圆,每个圆上的每一个点(x,y)都满足-100<x<100且-100<y<100,现在将某个人生成的1e5个圆的圆心和半径告…...

各种手型都合适,功能高度可定制,雷柏VT9PRO mini和VT9PRO游戏鼠标上手
去年雷柏推出了一系列支持4KHz回报率的鼠标,有着非常敏捷的反应速度,在游戏中操作体验十分出色。尤其是这系列4K鼠标不仅型号丰富,而且对玩家的操作习惯、手型适应也很好,像是VT9系列就主打轻巧,还有专门针对小手用户的…...

sql建库,建表基础操作
当涉及到SQL建库和建表操作时,以下是一个简单的示例: 1. 建库(创建数据库) sql复制代码 CREATE DATABASE mydatabase; 上述语句将创建一个名为mydatabase的数据库。 2. 选择数据库 在创建表之前,需要选择要在其中…...

算法训练营day32,贪心算法6
import "strconv" //738. 单调递增的数字 func monotoneIncreasingDigits(n int) int { str : strconv.Itoa(n) nums : []byte(str) length : len(nums) if length < 1 { return n } for i : length - 1; i > 0; i-- { //如果前一个数字比当前值大࿰…...

CTR之行为序列建模用户兴趣:DIN
在前面的文章中,已经介绍了很多关于推荐系统中CTR预估的相关技术,今天这篇文章也是延续这个主题。但不同的,重点是关于用户行为序列建模,阿里出品。 概要 论文:Deep Interest Network for Click-Through Rate Predict…...

Java使用Redis实现分页功能
分页功能实现应该是比较常见的,对于redis来说,近期刷题就发现了lrange、zrange这些指令,这个指令怎么使用呢? 我们接下来就来讲解下。 目录 指令简介lrangezrange Java实现Redis实现分页功能 指令简介 lrange lrange 是 Redis 中…...

Qt标准对话框设置
Qt标准对话框设置,设置字体、调色板、进度条等。 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this); }MainWindow::~MainWi…...