Python IO完成端口密钥混淆

Python IO完成端口密钥混淆,python,windows,winsock,ctypes,iocp,Python,Windows,Winsock,Ctypes,Iocp,我正在使用ctypes模块在Python中使用WindowsDLL API编写一个基于IO完成端口的服务器()。但这是API的一个非常直接的用法,这个问题是针对那些了解IOCP的人,而不是Python 根据我对CreateIoCompletionPort文档的理解,当您使用与创建的IOCP关联的文件句柄(在我的示例中是套接字)调用此函数时,可以指定“用户定义”的完成键。当您开始调用GetQueuedCompletionStatus时,会得到一个完成键值以及一个指向重叠对象的指针。完成键应标识重叠

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

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

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


我是否误解了完成键的工作原理,或者我必须在上面链接的源代码中出错?

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

看起来您没有使用
GetQueuedCompletionStatus
返回的
completionKey
执行任何操作

我猜你想要:

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


Python是否知道Win32 API正在异步使用在
CreateAcceptSocket
中创建的
OVERLAPPED
结构,并阻止它被GC调用?

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


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

我在日常实践中发现,最好只关注重叠的
结果,因为这不会改变。有效使用它的一种方法是使用以下内容:

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
这样,当您得到重叠的
结果时,您所要做的就是将其转换回
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()的实现。这里有一些细节,比如验证从
GetQueuedCompletionStatus
获得的
OVERLAPPED
指针,但同样,请参阅链接以获得一个好的实现方法。

完成键不是指针-它是一个类型为ULONG\u PTR的数字,它只意味着“一个指针大小的整数”因此,x86上是32位,x64上是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*/
...


并注意括号中的类型。

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

有些人使用扩展的重叠结构来处理这两个问题,并将他们需要的所有信息存储在扩展的重叠结构中。我总是存储一个引用计数对象,它将套接字包装在完成键中,并将引用计数数据缓冲区作为扩展重叠结构。如果你感兴趣,你可以看看


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

“假设我在CreateIoCompletionPort调用中使用重叠对象传递100作为完成键。当同一重叠对象的IO完成并通过GetQueuedCompletionStatus返回时,伴随它的完成键会大得多,并且与原始值100没有任何相似之处。“从我的问题和我为什么不在源代码中比较它。关于重叠对象被垃圾收集,您可能是对的,但这与问题无关。
HANDLE socket;
GetQueuedCompletionStatus(existed_io_completion_port, &io_bytes_done, (PULONG_PTR)&socket, &overlapped);