2

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

正如文档所说:

与 accept 函数不同,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;
    }
}

关于如何接收完成的任何建议?

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

4

1 回答 1

10

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

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

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

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

  1. 使用 set 创建一个套接字WSA_FLAG_OVERLAPPED
  2. 将套接字关联到您的 IOCP。

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

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

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

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

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

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

于 2013-11-13T15:19:52.640 回答