IO完成端口密钥混乱

时间:2009-07-24 08:52:38

标签: python windows winsock ctypes iocp

我正在使用ctypes模块在Python中使用Windows DLL API编写基于IO完成端口的服务器(source code here)。但这是对API的直接使用,这个问题针对的是那些了解IOCP而不是Python的人。

根据我对CreateIoCompletionPort的文档的理解,当您使用文件句柄(在我的情况下是套接字)调用此函数时,您指定了“用户定义的”完成键,您正在与创建的IOCP关联。当您调用GetQueuedCompletionStatus时,您将获得一个完成键值以及指向重叠对象的指针。完成键应标识重叠的对象和请求已完成的内容。

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

我是否误解了完成键的工作方式,或者我在上面链接的源代码中是否做错了?

6 个答案:

答案 0 :(得分:4)

我在日常实践中发现的是,最好只关注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(ver 1.42 header here)。有一些细节,例如验证从OVERLAPPED获得的GetQueuedCompletionStatus指针,但同样,请参阅链接以获得实施的好方法。

答案 1 :(得分:1)

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

completionKey返回的GetQueuedCompletionStatus似乎没有做任何事情。

我猜你想要:

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

编辑:

Python是否知道在OVERLAPPED中创建的CreateAcceptSocket结构是由Win32 API异步使用并阻止其进行GC的?

答案 2 :(得分:1)

您应该将完成键视为“每个连接”数据,并将(扩展的)重叠结构视为“每个i / o”操作。

有些人对BOTH使用扩展的重叠结构,并在扩展的重叠结构中存储他们需要的所有信息。我总是存储一个引用计数对象,它将我的套接字包装在完成键中,引用计数数据缓冲区作为扩展重叠结构。如果您有兴趣,可以查看some example IOCP code in C++ here

有效地,完成键只是一个不透明的数据句柄,I / O完成系统会在套接字完成时给你回复。

答案 3 :(得分:0)

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

对于接受的连接重叠数据包传递的完成密钥也是侦听套接字的完成密钥 - 而不是接受的套接字。

答案 4 :(得分:0)

完成键不是指针 - 它是一个类型为ULONG_PTR的数字,它只是表示“一个指针大小的整数”,因此它在x86上为32位,在x64上为64位。 typename令人困惑,但是当win32类型名称引用指针时,他们通过在名称的前面加一个P而不是结尾来做它。

答案 5 :(得分:0)

如果您的应用程序是多线程的,请确保您传递的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);

并注意括号中的类型。