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

IOCP结合WSASend使用时GetQueuedCompletionStatus返回998错误咨询

错误原因分析
  • 直接触发998(ERROR_NOACCESS,无效内存访问)的问题出在工作线程的GetQueuedCompletionStatus调用参数:
    你定义的PULONG_PTR my_CompletionKey是未初始化的野指针,而GetQueuedCompletionStatus的第三个参数是输出参数,要求传入ULONG_PTR类型变量的地址,函数会把IO对应的完成键写入你提供的地址。你传入野指针后,函数会尝试往非法内存地址写数据,直接触发访问错误。
  • 第二个严重内存问题:你传给WSASend的WSAOVERLAPPED结构体是main函数栈上的局部变量。IOCP异步操作的生命周期和调用函数的栈生命周期不一致,内核在IO完成时会往你提交的OVERLAPPED结构写数据,如果栈内存已经被释放/覆盖,就会触发内存访问错误。所有异步IO用的OVERLAPPED必须是堆分配,在IO完成通知处理完之后再释放。
  • 第三个内存问题:WSABUF指向的std::string内部缓冲区也是栈上局部变量,异步发送未完成时如果string被销毁,缓冲区地址就会失效,内核发送数据时读取无效内存也会报错。
  • 逻辑错误1:绑定套接字到完成端口时,第三个参数完成键传了NULL,后续从完成端口取出IO完成通知时,无法区分对应的是哪个套接字的操作,也无法关联套接字对应的上下文数据。
  • 逻辑错误2:没有判断WSAConnect的返回值,无法确认连接是否成功就直接调用WSASend。
  • 逻辑错误3:仅提交了WSASend异步请求,没有提交WSARecv异步请求,无法收到服务器返回的响应数据。
  • 逻辑错误4:工作线程中收到IO完成通知后没有做任何处理,也没有释放堆分配的OVERLAPPED等资源,会导致内存泄漏。
核心代码修正示例

首先修正工作线程的参数错误:

void My_func_for_Thread(HANDLE iocp)
{
    DWORD transferedBytes;
    ULONG_PTR completionKey; // 改成普通变量,传地址进去
    WSAOVERLAPPED* pOverlapped = NULL;
    while (1)
    {
        BOOL ret = GetQueuedCompletionStatus(iocp, &transferedBytes, &completionKey, &pOverlapped, INFINITE);
        if (ret == FALSE)
        {
            std::cout << "GetQueuedCompletionStatus错误,错误码:" << GetLastError() << std::endl;
            // 这里要处理错误,释放pOverlapped等资源
            continue;
        }
        // 在这里处理IO完成逻辑:判断是发送完成还是接收完成
        // 处理完后释放堆分配的pOverlapped、对应缓冲区
        delete pOverlapped;
    }
}

然后修正main函数中WSASend部分的OVERLAPPED和缓冲区分配:

// 发送请求部分改堆分配
WSAOVERLAPPED* pSendOverlapped = new WSAOVERLAPPED();
ZeroMemory(pSendOverlapped, sizeof(WSAOVERLAPPED));
// 缓冲区也要堆分配,保证在发送完成前不被销毁
std::string request_text_string = "GET / HTTP/1.1\r\nHost: government.ru\r\nConnection: keep-alive\r\n\r\n";
char* sendBuf = new char[request_text_string.size()];
memcpy(sendBuf, request_text_string.c_str(), request_text_string.size());
WSABUF my_WSABUF;
my_WSABUF.buf = sendBuf;
my_WSABUF.len = request_text_string.size();

int my_WSASend =  WSASend(my_WSASocketA, &my_WSABUF, 1, NULL, 0, pSendOverlapped, NULL);
if (my_WSASend == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
    // 只有错误不是IO_PENDING才是真的失败,要释放资源
    std::cout << "WSASend错误,错误码:" << WSAGetLastError() << std::endl;
    delete[] sendBuf;
    delete pSendOverlapped;
}

绑定完成端口时可以把套接字作为完成键传入,方便后续处理:

My_handle_Create_IOCP = CreateIoCompletionPort((HANDLE)my_WSASocketA, My_handle_IOCP, (ULONG_PTR)my_WSASocketA, 0);

补充说明:发送完成通知触发后,需要主动提交WSARecv异步请求,才能接收服务器返回的响应数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 22:36:03