IOCP 接受快递在连接时未创建完成

IOCP AcceptEx not creating completion upon connect

本文关键字:创建 连接 快递 IOCP      更新时间:2023-10-16

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

正如文档所说:

与接受函数不同,AcceptEx 函数使用重叠的 I/O。如果您的应用程序使用 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;
    }
}

关于如何获得完成的任何建议?

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

首先,

您在调用AcceptEx()上方的堆栈上声明的WSAOVERLAPPED和数据缓冲区在完成发生时将不存在(除非您在同一函数中调用GetQueuedCompletionStatus(),这将是一件奇怪的事情)。您需要动态分配它们或汇集它们。

其次,您声明在调用 AcceptEx() 后将套接字关联到完成端口。这是不对的。在打电话之前,您需要做这些事情 AcceptEx() .

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

现在,您可以使用侦听套接字和您创建的新的"accept"套接字发布许多AcceptEx()呼叫,如下所示:

  1. 创建设置WSA_FLAG_OVERLAPPED套接字。
  2. 将套接字关联到 IOCP。

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

完成时,您必须执行以下操作。

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

请注意,根据设计,AcceptEx()可用于接受新连接并在一次操作中从该连接返回初始数据(这会导致性能略好,因为您知道在开始做事之前总是需要一些数据,但如果您想保护拒绝服务攻击,只需连接而不是发送即可启动数据 - 我在这里写过这个)。

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

请注意,马丁·詹姆斯对你的问题的最初评论实际上是你正在寻找的答案。不要通过outBufLen - ((sizeof (sockaddr_in) + 16) * 2),通过0