是否有可能不会收到 WSASend 调用的完成?

Posted

技术标签:

【中文标题】是否有可能不会收到 WSASend 调用的完成?【英文标题】:Is it possible to not receive a completion for a WSASend call? 【发布时间】:2011-03-30 13:05:29 【问题描述】:

如标题所述,是否有可能成功 WSASend 在与 I/O 完成端口关联的套接字上调用除了线程结束之外的任何原因不发布完成?

我遇到了一个奇怪的情况,似乎没有为WSASend 发布完成,这导致套接字泄漏;应用程序认为套接字的发送仍处于挂起状态并拒绝释放它。

发送代码如下:

void CSocketServer::Write(
    Socket *pSocket,
    CIOBuffer *pBuffer) const

    pSocket->AddRef();

    pBuffer->SetOperation(IO_Write_Completed);
    pBuffer->SetupWrite();
    pBuffer->AddRef();

    DWORD dwFlags = 0;
    DWORD dwSendNumBytes = 0;

    if (SOCKET_ERROR == ::WSASend(
        pSocket->m_socket,
        pBuffer->GetWSABUF(), 
        1, 
        &dwSendNumBytes,
        dwFlags,
        pBuffer, 
        NULL))
    
        DWORD lastError = ::WSAGetLastError();

        if (ERROR_IO_PENDING != lastError)
        
            pSocket->OnConnectionError(WriteError, pBuffer, lastError);

            pSocket->WriteCompleted();  // this pending write will never complete...

            pSocket->Release();
            pBuffer->Release();
        
    
    // Note: even if WSASend returns SUCCESS an IO Completion Packet is 
    // queued to the IOCP the same as if ERROR_IO_PENDING was returned.
    // Thus we need no special handling for the non error return case.
    // See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q192800
    // for details.

【问题讨论】:

【参考方案1】:

您是否正在使用任何时髦的新功能,例如使用 FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 关闭成功呼叫的完成?

您是否对发送进行了任何形式的流量控制,还是只是在您喜欢的时候发送,并且发送次数不限?您可能看到的只是一个缓慢的完成,因为 TCP 堆栈正在进行拥塞控制并且还不能发送您的数据。如果您继续以不受控制的方式发送数据,您通常会遇到完成时间越来越长的情况。尤其是如果您以比 TCP 连接更快的速度发送数据,则可以成功地将数据发送到另一端,尤其是在 TCP 窗口不是那么大的情况下。请参阅此处:http://www.lenholgate.com/blog/2008/07/write-completion-flow-control.html 了解更多信息。

当然这可能只是你的发送逻辑中的一个错误,你能发布一些代码吗?

请注意,在使用 FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 时,WSARecv() 和 UDP 存在一个已知错误(因此与您的问题完全无关),如果数据报大于您提供的缓冲区,则会给出您描述的情况,并且WSARecv() 调用将生成 WSAEMOREDATA;看这里:http://www.lenholgate.com/blog/2010/01/file-skip-completion-port-on-success-and-datagram-socket-read-errors.html

【讨论】:

我想我可能正在使用一个 5-7 岁的、经过大量修改的服务器框架版本。这不是一个缓慢的完成,因为没有拥塞,但我看到一些从未释放的套接字。转储它们表明它们有一个参考和一个出色的写入。如果我等待 24 小时并再次转储它们,这些套接字仍然存在。这可能是某个地方的逻辑错误,我似乎无法找到它。 我不提供对免费代码的支持,甚至更少;)对于它的大量修改版本,但是,它听起来像一个逻辑错误。发布发出发送的代码? 我已经粘贴了Write 函数,不过我不确定其中有多少是你的。另一个线程将IO_Write_Request 操作发布到I/O 线程,由它处理并进入该函数。 是的,这几乎是我的全部内容,不太可能是其中的错误,因为有很多人使用免费框架几年都没有问题。我假设写入实际上并没有传递给对等方?或者它是否通过并且仍然没有在服务器中“完成”?您是否只是在 I/O 池中出现了死锁,或者丢失了 I/O 线程? 池中只有一个 I/O 线程,所以这应该不是问题。我不确定对方是否收到了写入,不幸的是,我无法在测试环境中重现它。不过,我会冒险猜测它不是,因为这总是发生在一个相当新的套接字上。完成的读取永远不会超过 2 或 3 次。【参考方案2】:

可以通过设置OVERLAPPED 结构的有效hEvent 的低位来阻止完成端口发布:

来自GetQueuedCompletionStatus 的文档:

即使你已经通过函数 a 与 a 关联的文件句柄 完成端口和有效的 OVERLAPPED 结构,一个应用程序可以防止 完成端口通知。这是 通过指定有效事件来完成 的 hEvent 成员的句柄 OVERLAPPED 结构,并设置其 低位。有效的事件句柄 其低位被设置保持 I/O 从排队到完成 完成端口。

【讨论】:

有趣。鉴于他使用的代码,这不太可能是他的问题的原因,除非他对该区域进行了大量“大量修改”......他正在使用的基本代码根本不使用重叠结构中的事件。跨度>

以上是关于是否有可能不会收到 WSASend 调用的完成?的主要内容,如果未能解决你的问题,请参考以下文章

我应该处理 WSASend() 可能不会发送所有数据的事实吗?

我可以反复调用 WSASend() 吗?

io 完成端口问题,每个 GetQueuedCompletionStatus 调用多个 wsarecv 或 wsasend

阻塞模式下的 WSASend() 是不是会导致将通知数据包放置在完成端口中?

连续性多个 WSASend() io 完成端口

具有多个缓冲区的 WSASend() - 可以完成不完整吗?