【问题标题】:AcceptEx() synchronous completion?AcceptEx() 同步完成?
【发布时间】:2018-03-03 21:18:07
【问题描述】:

我在学习服务器时正在使用 IO 完成端口和 AcceptEx(),并且正在研究 Len Holgate 的免费服务器框架来做到这一点。他有以下代码:

// Basically calls AcceptEx() via a previously obtained function pointer
if (!CMSWinSock::AcceptEx(
      m_listeningSocket,
      pSocket->m_socket,
      reinterpret_cast<void*>(const_cast<BYTE*>(pBuffer->GetBuffer())),
      bufferSize,
      sizeOfAddress,
      sizeOfAddress,
      &bytesReceived,
      pBuffer))
   {
      const DWORD lastError = ::WSAGetLastError();

      if (ERROR_IO_PENDING != lastError)
      {
         Output(_T("CSocketServerEx::Accept() - AcceptEx: ") + GetLastErrorMessage(lastError));

         pSocket->Release();           
         pBuffer->Release();
      }
   }
   else
   {
      // Accept completed synchronously. We need to marshal the data recieved over to the 
      // worker thread ourselves...

      m_iocp.PostStatus((ULONG_PTR)m_listeningSocket, bytesReceived, pBuffer);
   }

我对“接受同步完成”其他情况感到困惑。我已经尝试了很多次来获得这个代码路径(通过在我发出AcceptEx之前暂停代码,连接,然后恢复代码),但是每当我尝试调用总是以ERROR_IO_PENDING失败并且我得到了我的通知包。此外,我已经阅读了this MS knowledgebase article(我可能误解了)其中的说明

此外,如果 Winsock2 I/O 调用返回 SUCCESS 或 IO_PENDING,它 保证完成数据包将排队到 IOCP 时 I/O 完成

但是,我认为这不适用于AcceptEx(),因为dox 代表AcceptEx() 参数lpdwBytesReceived 的状态

仅当操作同步完成时才设置此参数。

看来它可以同步完成...有人可以告诉我如何 AcceptEx() 可以同步完成(即如何在我的服务器中复制它?)

【问题讨论】:

    标签: network-programming winsock winsock2 iocp


    【解决方案1】:

    此外,如果 Winsock2 I/O 调用返回 SUCCESSERROR_IO_PENDING,它 保证完成数据包将排队到 IOCP 时 I/O 完成

    如果完成端口与文件相关联,则适用于任何 I/O 请求。但从 windows vista 开始,这也取决于为文件句柄设置的notification mode

    但需要先从原生视图开始。

    默认情况下,如果FILE_SKIP_COMPLETION_PORT_ON_SUCCESS未设置,则返回NTSTATUS status存在3种情况:

    1. NT_SUCCESS(status)status &gt;= 0 - 即将完成
    2. NT_ERROR(status)status &gt;= 0xc0000000 - 不会完成
    3. NT_WARNING(status) or status &lt; 0xc0000000 - 不清楚 - 如果这个 来自 I/O 管理器的错误(比如 - STATUS_DATATYPE_MISALIGNMENT - 将 没有完成)。如果来自驱动程序的此错误(例如 STATUS_NO_MORE_FILES - 即将完成)。

    win32 层通常单独检查STATUS_PENDING 并在这种情况下返回ERROR_IO_PENDING(但存在异常,如ReadDirectoryChangesW)。否则如果NT_ERROR(status) api 返回失败并设置错误代码。否则返回成功。可见那个案例NT_WARNING(status) 被认为是成功的,但是在这种情况下,如果来自 I/O 管理器的错误,将不会完成。如果参数不正确,I/O 通常会从NT_ERROR(status) 范围返回错误。只有我知道的情况(对于异步 api)-STATUS_DATATYPE_MISALIGNMENT 可以返回,以防错误对齐的缓冲区,当 I/O 管理器有关于缓冲区对齐的特殊知识时。在NtNotifyChangeDirectoryFileReadDirectoryChangesW for win32)或NtQueryDirectoryFile(没有对应的win32 api)。所以只有我知道什么时候不会完成的情况,当 win32 返回成功时 - 使用未对齐的 lpBuffer 调用 ReadDirectoryChangesW(它必须是 DWORD 对齐的) - 在这种情况下,I/O 管理器只返回 @ 987654345@ 但 win32 层将此解释为成功代码并返回 true。但在这种情况下不会完成。但是这种情况很少见,您可能需要为此使用错误的对齐结构。所以总的来说是的:

    默认情况下,如果 I/O 调用返回 SUCCESSERROR_IO_PENDING 将排队进入端口的完成条目。 (我尝试描述的特殊例外情况

    如果我们在文件对象上设置FILE_SKIP_COMPLETION_PORT_ON_SUCCESS(注意这是每个文件对象,而不是每个文件句柄 - 文档不完全在这里)都会变得更加简单和高效 - 完成条目将排队到端口 - 何时且仅当 I/O 请求返回 STATUS_PENDINGERROR_IO_PENDING 来自 win32 视图(ReadDirectoryChangesW 除外(可能是其他 api ?)其中 win32 层只是丢失了返回码信息)

    但是,我认为这不适用于AcceptEx()

    你错了。这,我怎么说,适用于任何 io 请求。 “仅当操作同步完成时才设置此参数。” - 那又怎样?


    如果查看代码 sn-p,可以清楚地看到代码假设 - 如果 AcceptEx 同步完成并且没有发生错误 - 将没有 io 完成。或SetFileCompletionNotificationModes(m_listeningSocket, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) 调用或代码错误 - 在这种情况下将是 io 完成,不需要 m_iocp.PostStatus - 这是致命错误。但是我怀疑代码使用了FILE_SKIP_COMPLETION_PORT_ON_SUCCESS - 所以它错了。但从未引发错误,因为AcceptEx(下划线ioctl)的驱动程序端实现永远不会返回STATUS_SUCCESS:它检查参数-如果错误-只返回一些错误,否则总是返回STATUS_PENDING。结果,对于异步套接字 AcceptEx 永远不会返回 true 并且代码永远不会跳转到错误,否则。但无论如何代码是错误的。我也认为设计不是最好的——如果我们确定没有完成——最好直接调用完成例程并返回错误代码而不是Release()(这将在完成例程中完成)或PostStatus——什么帖子?! - 直接打电话。


    AcceptEx()如何同步完成

    非常简单 - 如果 m_listeningSocket同步 文件对象的句柄。但是在这种情况下,您不能将 IOCP 绑定到文件(只能在异步文件对象的情况下绑定)。


    关于lpdwBytesReceived参数-系统复制InformationIO_STATUS_BLOCK的成员或者如果想要OVERLAPPED.InternalHigh,以防操作完成。如果挂起返回 - 此数据根本未准备好且未填充。你得到了 io 在完成中返回的实际字节数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多