You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GetQueuedCompletionStatus如何无需自定义套接字结构体读取客户端消息

回答

可以实现,但仅能用于极简演示场景,没有实际生产价值。原因是IOCP完成通知需要携带IO操作对应的套接字、缓冲区地址、操作类型等上下文信息,没有自定义结构体的情况下只能用全局变量存储这些信息,无法支持多客户端、多类型IO操作的场景。

实现原理

不使用自定义结构体时,我们可以利用GetQueuedCompletionStatus的两个原生输出参数传递基础信息:

  • 把客户端套接字直接作为完成键在绑定套接字到IOCP时传入,从*lpCompletionKey参数直接拿到对应的客户端套接字
  • 用全局的OVERLAPPED结构和全局的接收缓冲区处理IO操作,不需要从OVERLAPPED扩展自定义数据

极简示例代码

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 1024
// 全局变量,仅用于演示,生产环境禁止这么用
SOCKET g_ClientSock = INVALID_SOCKET;
OVERLAPPED g_Overlapped = {0};
char g_RecvBuf[BUF_SIZE] = {0};
HANDLE g_Iocp = NULL;

// 工作线程
DWORD WINAPI WorkerThread(LPVOID lpParam) {
    DWORD bytesTransferred;
    ULONG_PTR completionKey;
    LPOVERLAPPED pOverlapped;
    while (1) {
        BOOL ret = GetQueuedCompletionStatus(
            g_Iocp,
            &bytesTransferred,
            &completionKey,
            &pOverlapped,
            INFINITE
        );
        if (!ret || bytesTransferred == 0) {
            // 客户端断开或出错,演示直接退出
            printf("客户端断开连接\n");
            closesocket((SOCKET)completionKey);
            return 0;
        }
        // completionKey就是我们绑定的客户端套接字
        SOCKET clientSock = (SOCKET)completionKey;
        g_RecvBuf[bytesTransferred] = '\0';
        printf("收到客户端[%d]消息:%s\n", clientSock, g_RecvBuf);
        // 继续投递下一个接收请求
        DWORD flags = 0;
        WSABUF wsaBuf = {BUF_SIZE, g_RecvBuf};
        ZeroMemory(&g_Overlapped, sizeof(OVERLAPPED));
        WSARecv(clientSock, &wsaBuf, 1, NULL, &flags, &g_Overlapped, NULL);
    }
    return 0;
}

int main() {
    // 初始化Winsock
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);
    // 创建监听套接字
    SOCKET listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(12345);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(listenSock, (SOCKADDR*)&addr, sizeof(addr));
    listen(listenSock, 10);
    // 创建IOCP
    g_Iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    // 创建工作线程
    CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
    printf("服务端启动,监听端口12345...\n");
    // 接受客户端连接,演示仅接受一个客户端
    g_ClientSock = accept(listenSock, NULL, NULL);
    printf("新客户端连接:%d\n", g_ClientSock);
    // 把客户端套接字绑定到IOCP,完成键直接传套接字本身
    CreateIoCompletionPort((HANDLE)g_ClientSock, g_Iocp, (ULONG_PTR)g_ClientSock, 0);
    // 投递第一个接收请求
    DWORD flags = 0;
    WSABUF wsaBuf = {BUF_SIZE, g_RecvBuf};
    WSARecv(g_ClientSock, &wsaBuf, 1, NULL, &flags, &g_Overlapped, NULL);
    // 阻塞主线程,演示用
    getchar();
    // 清理资源
    CloseHandle(g_Iocp);
    closesocket(listenSock);
    WSACleanup();
    return 0;
}

注意事项

  • 该示例仅用于验证逻辑,不能用于生产环境,全局变量会导致多客户端场景下数据覆盖
  • 没有处理重叠操作的错误情况,也没有处理GetQueuedCompletionStatus返回的各种异常状态
  • 实际开发中还是建议使用自定义扩展OVERLAPPED结构体的方式传递上下文,这是IOCP的标准实现方式

内容的提问来源于stack exchange,提问作者sakdkjkj jjsdjds

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 03:45:06