6

我正在使用 ctypes 模块在 Python 中使用 Windows DLL API编写基于 IO 完成端口的服务器(此处为源代码)。但这是 API 的一个非常直接的用法,这个问题是针对那些了解 IOCP 而不是 Python 的人。

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

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

我是否误解了完成键的工作原理,还是我在上面链接的源代码中做错了?

4

6 回答 6

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 的 Windows版 ASIO 实现(此处为 1.42 版标头)。有一些细节,例如验证OVERLAPPED您从中获得的指针GetQueuedCompletionStatus,但同样,请参阅链接以获取实现的好方法。

于 2010-02-24T23:20:48.997 回答
2

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

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

实际上,完成键只是一个不透明的数据句柄,当套接字上发生完成时,I/O 完成系统将返回给您。

于 2011-10-20T06:35:35.547 回答
1

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

看起来您没有对completionKeyas 返回的内容做任何事情GetQueuedCompletionStatus

我猜你想要:

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

编辑:

Python是否以某种方式知道Win32 API正在异步使用其中OVERLAPPED创建的结构并阻止它被GC?CreateAcceptSocket

于 2009-07-24T09:50:51.543 回答
0

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

于 2010-04-02T07:01:23.943 回答
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);

并注意括号中的类型。

于 2011-10-19T21:34:49.403 回答
0

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

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

于 2009-07-25T05:58:45.863 回答