IOCP(Input Output Completion Port)输入输出完成端口。其实就是基于重叠I/O的一种改进的模型。
重叠I/O具有缺点:重复调用非阻塞模式的accpet函数和以进入alertablewait状态为目的的SleepEx函数会影响程序性能。
而IOCP提供的解决方案便是:让主线程调用accept函数,单独创建至少一个线程来负责所有I/O的前后处理。
但请不要过分关注在线程上,主要还是如下问题:
1.I/O是否以非阻塞模式工作?
2.如何确定非阻塞模式的I/O是否完成?
IOCP会将已完成的I/O信息注册到CP对象(Completion Port完成端口),而我们就可以通过CP对象来获取I/O是否完成的信息,所以有下面两项工作:
此时的套接字必须赋予重叠属性。
- #include
-
- HANDLE CreateIoCompletionPort(
- HANDLE fileHandle, //创建CP对象时传递INVALID_HANDLE_VALUE
- HANDLE ExistingCompletionPort, //创建CP对象时传递NULL
- ULONG_PTR CompletionKey, //创建CP对象时传递0
- DWORD NumberOfConcurrentThreads //分配给CP对象的用于处理I/O的线程数。
- //例如:该参数为2时,说明分配给CP对象的可以同时运行的线程数最多为2个
- //如果为0时,那么系统中CPU的个数就是可同时运行的最大线程数
- );
- 成功返回CP对象句柄
- 失败返回NULL
- #include
-
- HANDLE CreateIoCompletionPort(
- HANDLE FileHandle, //要连接到CP对象的套接字句柄
- HANDLE ExistingCompletionPort, //要连接套接字的CP对象句柄
- ULONG_PTR CompletionKey, //传递已完成I/O相关信息
- DWORD NumberOfConcurrentThreads //无论传递何值,只要第二个参数非NULL就会被忽略
- );
- 成功返回CP对象句柄
- 失败返回NULL
函数功能:将FileHandle句柄指向的套接字和ExistingCompletionPort指向的CP对象相连。
调用此函数后:只要针对FileHandle的I/O完成,相关信息就会注册到ExistingCompletionPort里。
注意:第三个参数“传递已完成I/O相关信息”的意思是,你可以像重叠I/O里使用Complition routine来确认I/O方式里把相关信息填写到hEvent里的那样,写入其他信息,这样当I/O完成就可以获取了。
- #include
-
- BOOL GetQueuedCompletionStatus(
- HANDLE CompletionPort, //注册有已完成I/O信息的CP对象句柄
- LPDWORD lpNumberOfBytes, //保存I/O过程中传输的数据大小的变量地址值
- PULONG_PTR lpCompletionKey, //保存CreateIoCompleytionPort函数第三个参数值得变量地址值
- LPOVERLAPPED* lpOverlapped, //保存调用WSASend、WSARecv函数时传递的OVERLAPPED结构体地址的变量地址值
- DWORD dwMilliseconds //超时信息,超过该指定时间后将返回FALSE并跳出函数。
- //传递INFINITE时,程序将阻塞,直到已完成I/O信息写入CP对象
- );
- 成功返回TRUE
- 失败返回FALSE
注意:
思路:每连接一个客户端就创建一个线程,然后主线程里先接收一次数据,在子线程里通过GetQueuedCompletionStatus函数阻塞住线程,判断I/O状态,接着把接收的数据发送给客户端,再次进入接收状态,如此循环通信。
变量:
struct ClientInfo结构体:存有套接字和套接字地址族信息,在CreateIoCompletionPort函数里,建立套接字和CP的连接的时候,当做第三参数传入
struct CPInfo结构体:存有一个OVERLAPPED、WSABUF信息,以及还有一个int型用来判断当前是RECV还是SEND,在执行WSARecv函数时当做第六个参数进行传入。运用下面的知识点,所以可以在子线程执行GetQueuedCompletionStatus函数时,取得的第一个成员的地址,也就是这整个结构体的地址。
知识点:结构体变量地址值与结构体第一个成员的地址值相同。
struct CPInfo { OVERLAPPED overlapped; WSABUF wsabuf; int mode; //0:RECV 1:SEND }; CPInfo data; if(&data==&data.overlapped) { std::cout<<"TRUE"< } else { std::cout<<"FALSE"< } 输出TRUE
#define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include #include #include #include #include struct ClientInfo { SOCKET socket; sockaddr_in socketAddr; }; struct CPInfo { OVERLAPPED overlapped; WSABUF wsabuf; int mode; //0:RECV 1:SEND }; unsigned WINAPI threadClient(void* arg); int main() { WSADATA wsaData; if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) { std::cout << "start up fail!" << std::endl; return 0; } SOCKET server = WSASocket(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if (server == INVALID_SOCKET) { std::cout << "socket fail!" << std::endl; return 0; } int mode = 1; ioctlsocket(server, FIONBIO, (u_long*)&mode); sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(9130); if (SOCKET_ERROR == bind(server, (sockaddr*)&serverAddr, sizeof(serverAddr))) { std::cout << "bind fail!" << std::endl; return 0; } if (SOCKET_ERROR == listen(server, 2)) { std::cout << "listen fail!" << std::endl; return 0; } while (true) { sockaddr_in clientAddr; memset(&clientAddr, 0, sizeof(clientAddr)); int clientAddrLen = sizeof(clientAddr); SOCKET client = accept(server, (sockaddr*)&clientAddr, &clientAddrLen); if (client == SOCKET_ERROR) { if (WSAGetLastError() == WSAEWOULDBLOCK) //说明此时没有客户端连接 { continue; } std::cout << "accept fail!" << std::endl; } else { HANDLE cpObject = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (cpObject == NULL) { std::cout << "Create CP fail!" << std::endl; continue; } ClientInfo* clientinfo = new ClientInfo(); clientinfo->socket = client; clientinfo->socketAddr = clientAddr; CreateIoCompletionPort((HANDLE)client, cpObject, (ULONG_PTR)clientinfo, 0); unsigned threadId; if (0 == _beginthreadex(NULL, 0, threadClient, (void*)&cpObject, 0, &threadId)) //创建一个线程 { std::cout << "thread create fail!" << std::endl; continue; } CPInfo* cpinfo = new CPInfo(); cpinfo->mode = 0; memset(&cpinfo->overlapped, 0, sizeof(cpinfo->overlapped)); char buff[1024]; cpinfo->wsabuf.buf = buff; cpinfo->wsabuf.len = sizeof(buff); DWORD readLen; DWORD flag = 0; WSARecv(client, &cpinfo->wsabuf, 1, &readLen, &flag, &cpinfo->overlapped, NULL); } } closesocket(server); WSACleanup(); } unsigned WINAPI threadClient(void* arg) { HANDLE cpObject = *(HANDLE*)arg; CPInfo* cpinfo; ClientInfo* clientinfo; while (true) { DWORD readLen; GetQueuedCompletionStatus(cpObject, &readLen, (PULONG_PTR)&clientinfo, (LPOVERLAPPED*)&cpinfo, INFINITE); if (readLen == 0) { std::cout << "客户端:" << inet_ntoa(clientinfo->socketAddr.sin_addr) << "断开连接!" << std::endl; break; } if (cpinfo->mode == 0) //recv { std::cout << "客户端发来的消息:" << cpinfo->wsabuf.buf << std::endl; DWORD flag = 0; cpinfo->mode = 1; WSASend(clientinfo->socket, &cpinfo->wsabuf, 1, &readLen, flag, &cpinfo->overlapped, NULL); CPInfo* cpinfo2 = new CPInfo(); cpinfo2->mode = 0; memset(&cpinfo2->overlapped, 0, sizeof(cpinfo2->overlapped)); char buff[1024]; cpinfo2->wsabuf.buf = buff; cpinfo2->wsabuf.len = sizeof(buff); DWORD readLen2; WSARecv(clientinfo->socket, &cpinfo2->wsabuf, 1, &readLen2, &flag, &cpinfo2->overlapped, NULL); } else //send { delete cpinfo; } } CloseHandle(cpObject); closesocket(clientinfo->socket); return 0; }- 相关阅读:
Toronto Research Chemicals BTK抑制剂丨ACP-5197
广州华锐互动:VR互动实训内容编辑器助力教育创新升级
开源语言大模型的正确姿势
redis内存描述
ERROR: [Synth 8-439] module ‘xxx‘ not found not found 错误解决办法
全光网络技术、标准、应用现状及展望
【Oracle】Oracle系列之九--Oracle常用函数
网站安全-行为式验证码
有了低代码,二次开发都不是事!
2022 Gartner RPA魔力象限发布,两家国产厂商入选,超自自动化成重点
- 原文地址:https://blog.csdn.net/A_ns_wer_/article/details/133387387