IO完成端口密钥混淆

我正在使用ctypes模块在Python中使用Windows DLL API编写基于IO完成端口的服务器( 源代码 )。 但这是对API的直接使用,这个问题针对那些掌握IOCP知识的人,而不是Python。

正如我理解的CreateIoCompletionPort的文档,你指定你的“用户定义的”完成键,当你用一个文件句柄(在我的情况下是一个套接字)与创build的IOCP关联调用此函数。 当你开始调用GetQueuedCompletionStatus时,你会得到一个完成键值和一个指向重叠对象的指针。 完成键应该确定重叠的对象和请求已经完成。

但是,假设我在CreateIoCompletionPort调用中使用重叠对象传入100作为完成键。 当相同的重叠对象的IO完成并且通过GetQueuedCompletionStatus返回时,它所附带的完成键要大得多,并且与原始值100毫不相似。

我误解完成密钥是如何工作的,还是我在上面链接的源代码中做错了?

Solutions Collecting From Web of "IO完成端口密钥混淆"

我在日常实践中发现,最好只关注OVERLAPPED结果,因为这将保持不变。 你可以有效地使用它的一种方法是有如下的东西:

 struct CompletionHandler { OVERLAPPED dummy_ovl; /* Stuff that actually means something to you here */ }; 

当您向IOCP发布某些内容时(无论是通过I / O调用还是通过Win32 API发布信息),首先创建一个CompletionHandler对象,用于跟踪调用,并将该对象的地址转换为OVERLAPPED*

 CompletionHander my_handler; // Fill in whatever you need to in my_handler // Don't forget to keep the original my_handler! // I/O call goes here, and for OVERLAPPED* give: (OVERLAPPED*)&my_handler 

这样,当你得到OVERLAPPED结果时,你所要做的就是将其重新转换为CompletionHandler并且瞧! 你有你的电话的原始上下文。

 OVERLAPPED* from_queued_completion_status; // Actually get a value into from_queued_completion_status CompletionHandler* handler_for_this_completion = (CompletionHandler*)from_queued_completion_status; // Have fun! 

有关实际环境中的更多详细信息,请参阅Boost的ASIO for Windows实现( 此处为版本1.42标题 )。 有一些细节,比如验证来自GetQueuedCompletionStatusOVERLAPPED指针,但是请再次参阅链接以获得实现的好方法。

GetQueuedCompletionStatus返回两个东西,一个OVERLAPPED结构和一个完成键。 完成键表示每个设备的信息,而OVERLAPPED结构表示每个呼叫信息。 完成键应该与CreateIoCompletionPort调用中给出的内容相匹配。 通常,您可以使用指向包含有关连接信息的结构的指针作为完成键。

它看起来像你没有做任何与GetQueuedCompletionStatus返回的completionKey

我猜你想要:

 if completionKey != acceptKey: Cleanup() ... 

编辑:

Python以某种方式知道在CreateAcceptSocket创建的OVERLAPPED结构是由Win32 API异步使用,并防止它被GC'd?

您应该将完成密钥视为“每个连接”数据,将(扩展的)重叠结构视为“每个I / O”操作。

有些人对BOTH使用扩展的重叠结构,在扩展的重叠结构中存储他们需要的所有信息。 我一直存储一个引用计数对象,它将套接字封装在完成键中,引用计数数据缓冲区作为扩展重叠结构。 如果你感兴趣,你可以在这里看到一些C ++的IOCP代码 。

完成键实际上只是一个不透明的数据处理,I / O完成系统在套接字上发生完成时会给你一个数据。

问题在于我如何传递完成键。 完成关键参数是一个指针,但它传回的指针不是指向的值 – 至少有点让我困惑。

此外,为接受的连接重叠数据包传递的完成密钥是监听套接字的 – 不是接受的套接字。

完成键不是一个指针 – 它是一个ULONG_PTR类型的数字,它的意思是“一个指针大小的整数”,所以它在x86上是32位,在64位上是64位。 typename是令人困惑的,但是当win32 typenames指向一个指针的时候,他们通过在名字前面加上P来实现,而不是结束。

如果你的应用程序是多线程的,确保你传递的CompletionKey是一个常量,或者是一个指向堆的对象的指针值,而不是堆栈。 在你的例子中,100被作为常量传递,你说任何改变都是错的。 但是对于这个问题,可能是因为您在CreateIoCompletionPort中传递了一个套接字句柄,而没有在GetQueuedCompletionStatus中处理的引用,以便对其进行检索。 你可以做

 HANDLE socket; CreateIoCompletionPort((HANDLE)socket, existed_io_completion_port, (ULONG_PTR)socket, 0); /*some I/Os*/ ... 

 HANDLE socket; GetQueuedCompletionStatus(existed_io_completion_port, &io_bytes_done, (PULONG_PTR)&socket, &overlapped); 

并注意括号中的类型。