AcceptEx()同步完成?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcceptEx()同步完成?相关的知识,希望对你有一定的参考价值。

我正在使用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,则可确保在I / O完成时完成数据包将排队到IOCP

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

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

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

答案

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

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

但首先需要从本机视图开始。

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

  1. NT_SUCCESS(status)status >= 0 - 将完成
  2. NT_ERROR(status)status >= 0xc0000000 - 将无法完成
  3. NT_WARNING(status) or status < 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) - 当I / O管理器具有关于缓冲区对齐的特殊知识时,可以在错误的对齐缓冲区的情况下返回STATUS_DATATYPE_MISALIGNMENT。在NtNotifyChangeDirectoryFileReadDirectoryChangesW for win32)或NtQueryDirectoryFile(没有相应的win32 api)。所以只有我知道什么时候才能完成的情况,当win32返回成功时 - 用未对齐的lpBuffer调用ReadDirectoryChangesW(它必须是DWORD对齐的) - 在这种情况下,I / O管理器只返回STATUS_DATATYPE_MISALIGNMENT但是win32层将此解释为成功代码并且返回true。但在这种情况下将无法完成。但这种情况很少发生,您可能需要使用错误的对齐结构。所以一般是的:

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

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

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

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


如果查看代码片段,清除可见代码假设 - 如果AcceptEx完成同步并且没有发生错误 - 将不会完成。或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参数 - 系统复制Information成员IO_STATUS_BLOCK或如果想要OVERLAPPED.InternalHigh,以防操作刚刚完成。如果挂起返回 - 这些数据根本没有准备好,也没有填写。你有完成后io返回的实际字节数

以上是关于AcceptEx()同步完成?的主要内容,如果未能解决你的问题,请参考以下文章

AcceptEx与完成端口结合实例

关闭尚未完成 AcceptEx 的套接字 - 如果以及如何?

如何知道完成数据包是针对 WSASend() 还是 WSARecv() 还是 AcceptEx()?

IOCP、AcceptEx、重叠和 WSAEINVAL

我应该使用 AcceptEx() 还是 WSAAccept()?

Apache - AH00341:winnt_accept:异步 AcceptEx 失败