IOCP 接受快递在连接时未创建完成
IOCP AcceptEx not creating completion upon connect
我目前正在尝试一些用于套接字编程的新库(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()
.
- 创建设置
WSA_FLAG_OVERLAPPED
套接字。 - 将其绑定到您要侦听的地址。
- 调用收听所需的积压工作。
- 使用侦听套接字和对
WSAIoctl
的调用动态加载AcceptEx()
(不是绝对必要的,您显示的代码应该可以工作,但这样您可以确保从相同的底层 winsock 提供程序获取侦听套接字,并且它支持 AcceptEx()。 - 加载
GetAcceptExSockaddrs()
的方式与加载AcceptEx()
的方式相同 - 接受完成后将需要它。 - 将侦听套接字关联到 IOCP。
现在,您可以使用侦听套接字和您创建的新的"accept"套接字发布许多AcceptEx()
呼叫,如下所示:
- 创建设置
WSA_FLAG_OVERLAPPED
套接字。 - 将套接字关联到 IOCP。
如上所述,您需要确保缓冲区和 OVERLAPPED 在每次调用时都是唯一的,并且一直持续到完成为止。
完成时,您必须执行以下操作。
- 使用侦听套接字作为数据,在接受的套接字上调用带有
SO_UPDATE_ACCEPT_CONTEXT
的setsockopt()
... - 使用
GetAcceptExSockaddrs()
取消阻止您的地址。 - 处理任何数据(如果在缓冲区中为数据分配了足够的空间)。
请注意,根据设计,AcceptEx()
可用于接受新连接并在一次操作中从该连接返回初始数据(这会导致性能略好,因为您知道在开始做事之前总是需要一些数据,但如果您想保护拒绝服务攻击,只需连接而不是发送即可启动数据 - 我在这里写过这个)。
如果您不希望AcceptEx()
等待数据到达,那么只需提供一个仅足够大的数据缓冲区以返回地址,并将 0 作为"缓冲区大小"传递。这将导致AcceptEx()
像重叠的accept()
一样运行,并在建立连接后立即返回。
请注意,马丁·詹姆斯对你的问题的最初评论实际上是你正在寻找的答案。不要通过outBufLen - ((sizeof (sockaddr_in) + 16) * 2)
,通过0
。
- 在 libcurl 连接池中预创建连接
- C++套接字客户端到 Python 服务器未创建连接
- 如何在多个线程中创建 QSql数据库连接时防止名称冲突
- 如何在没有侦听器的情况下创建 TCP 连接?
- 在线程 A 中创建一个 std::thread 对象,在线程 B 中连接
- 使用提升连接已创建的命名管道
- 将 int 转换为字符串,然后连接另一个变量以创建完整扩展名,然后将其转换为 const_char*
- 无法创建栅格堆栈,因为我无法连接字符串(dir_name + 文件名)
- 尝试连接两种不同类型的结构来创建链表
- 使用OCCI-创建连接
- OCCI 19.3.0:创建连接崩溃并出现OCCIUTF16
- 尝试创建表面网格,但遇到连接问题
- 如何在GRPC客户端中创建到一个特定地址C++多个连接
- 使用QT C 为Sevaral表单创建常见的数据库连接
- boost::asio::async_connect 不仅创建一个 TCP 连接,还创建两个
- 在 opencv 中从 Mat 图像创建多个子图像?尝试为每个连接的组件创建子图像
- 正在创建多个TCP套接字连接
- 使用Winsock创建SSL连接
- 使用 vector::itrator 创建和连接 pthreads<pthread_t>
- Visual Studio 2010 在创建连接时为 Oracle OCCI 11g 提供报告"Access violation"