IOCP AcceptEx 在连接时未创建完成

Posted

技术标签:

【中文标题】IOCP AcceptEx 在连接时未创建完成【英文标题】:IOCP AcceptEx not creating completion upon connect 【发布时间】:2013-11-13 14:23:02 【问题描述】:

我目前正在尝试一些用于套接字编程的新库 (IOCP)。我偶然发现了AcceptEx 启用异步连接的功能。

正如文档所说:

AcceptEx 函数使用重叠 I/O,与 accept 函数不同。如果您的应用程序使用 AcceptEx,它可以用相对较少的线程数为大量客户端提供服务。与所有重叠的 Windows 函数一样,Windows 事件或完成端口都可以用作完成通知机制。

但是当客户端连接时,我没有收到任何完成。但是,当客户端发送数据时,我确实会完成..

这是我的代码:

DWORD dwBytes;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
int iResult = WSAIoctl(m_hSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
    &GuidAcceptEx, sizeof (GuidAcceptEx), 
    &m_lpfnAcceptEx, sizeof (m_lpfnAcceptEx), 
    &dwBytes, NULL, NULL);

if (iResult == SOCKET_ERROR)

    CloseSocket();

然后:

WSAOVERLAPPED olOverlap;
memset(&olOverlap, 0, sizeof (olOverlap));
char lpOutputBuf[1024];
int outBufLen = 1024;
DWORD dwBytes;

BOOL bRet = m_lpfnAcceptEx( m_hSocket, hSocket, lpOutputBuf,
             outBufLen - ((sizeof (sockaddr_in) + 16) * 2),
             sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, 
             &dwBytes, &olOverlap);
if ( bRet == FALSE )

    DWORD dwRet = WSAGetLastError();
    if( dwRet != WSA_IO_PENDING )
    
        return dwRet;
    

有什么建议可以接收完成吗?

编辑: 我在 m_lpfnAcceptEx() 之后将 hSocket 绑定到完成端口

【问题讨论】:

非零 dwReceiveDataLength,所以 MSDN '如果 dwReceiveDataLength 为零,接受连接不会导致接收操作。相反,只要连接到达,AcceptEx 就会完成,无需等待任何数据。不适用。此外,这段代码看起来不太像 C++。 outBufLen - ((sizeof (sockaddr_in) + 16) * 2) 在我的情况下是 960,所以这不是问题.. 这不是问题:) 【参考方案1】:

首先,当完成发生时,您在调用AcceptEx() 上方的堆栈上声明的WSAOVERLAPPED 和数据缓冲区将不存在(除非您在同一个函数中调用GetQueuedCompletionStatus(),这将有点奇怪)。您需要动态分配它们或将它们池化。

其次,您声明在调用AcceptEx() 后将套接字与完成端口相关联。那是错误的。您需要在致电AcceptEx() 之前完成这些事情。

    创建一个设置了WSA_FLAG_OVERLAPPED 的套接字。 将其绑定到您要收听的地址。 用你想要的积压来调用它。 使用侦听套接字动态加载AcceptEx() 并调用WSAIoctl(不是绝对必要的,您显示的代码应该可以工作,但是这样您可以确保您从同一个底层winsock 提供程序获取您的侦听套接字,并且它支持 AcceptEx()。 以与加载 AcceptEx() 相同的方式加载 GetAcceptExSockaddrs() - 一旦接受完成,您将需要它。 将侦听套接字关联到您的 IOCP。

现在您可以使用侦听套接字和您创建的新“接受”套接字发布多个 AcceptEx() 调用,如下所示:

    创建一个设置了WSA_FLAG_OVERLAPPED 的套接字。 将套接字关联到您的 IOCP。

如上所述,您需要确保缓冲区和 OVERLAPPED 在每次调用时都是唯一的,并且持续到完成为止。

完成后,您必须执行以下操作....

    使用侦听套接字作为数据在接受的套接字上调用setsockopt()SO_UPDATE_ACCEPT_CONTEXT... 使用 GetAcceptExSockaddrs() 取消阻止您的地址。 处理任何数据(如果您在缓冲区中为数据分配了足够的空间)。

请注意,按照设计,AcceptEx() 可用于接受新连接并在一次操作中从该连接返回初始数据(这会在您知道您总是需要一些数据之前,这会导致性能稍好一些)开始做事,但如果你想防御拒绝服务攻击,这种攻击可以通过连接而不是发送数据来发起——我写过这个here)。

如果您不希望AcceptEx() 等待数据到达,那么只需提供一个数据缓冲区,该缓冲区仅足以返回地址,并将 0 作为“缓冲区大小”传递。这将导致AcceptEx() 像重叠的accept() 一样运行,并在建立连接后立即返回。

请注意,Martin James 对您的问题的最初评论实际上就是您正在寻找的答案。不要传outBufLen - ((sizeof (sockaddr_in) + 16) * 2),传0

【讨论】:

干得好,+1 超越了问题并解释了 OP C 样式代码的其他一些问题,(啊!)。 我实际上认为,当接受完成时,重叠和数据缓冲区超出范围这一事实很可能是他没有得到任何完成的原因。我预计可能会发生访问冲突,并且完成只是被丢弃了。 OP: '但是当客户端发送数据时我确实得到了一个完成' 成功了,感谢您的输入!不过有一个问题,我应该在完成之后还是在 AcceptEx 之后调用 WSARecv。目前我正在这样做,我收到错误 10057,这似乎是正确的,因为尚未建立连接。我看对了吗? 在 acceptEx 完成后调用 WSARecv 并且您已通过调用 setsockopt() 更新了已接受套接字的接受上下文。

以上是关于IOCP AcceptEx 在连接时未创建完成的主要内容,如果未能解决你的问题,请参考以下文章

AcceptEx与完成端口结合实例

在接受后 TCP IOCP 不会收到

IOCP陷阱

网络模型之IOCP与扩展函数

没有完成键的 IOCP 通知

如何使用 IOCP 获取客户端真实 IP 地址和端口?