使用 ReadFile 异步读取管道

Posted

技术标签:

【中文标题】使用 ReadFile 异步读取管道【英文标题】:Reading pipe asynchronously using ReadFile 【发布时间】:2018-07-01 23:33:42 【问题描述】:

我想我需要澄清一下如何从命名管道中读取并让它立即返回,无论是否有数据。我看到的是 ReadFile 失败,正如预期的那样,但 GetLastError 返回 ERROR_IO_PENDING 或 ERROR_PIPE_NOT_CONNECTED,它会一直执行此操作,直到我周围的代码超时。即使数据实际上已经到达,我也会收到这些错误。我通过检查我的读取缓冲区并查看我的期望来知道这一点。并且管道继续工作。我怀疑我没有正确使用重叠结构,我只是将所有字段设置为零。我的代码如下所示:

gPipe = CreateFile(gPipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
pMode = PIPE_READMODE_MESSAGE; 
bret = SetNamedPipeHandleState(gPipe, &pMode, NULL, NULL);

OVERLAPPED ol;
memset(&ol, 0, sizeof(OVERLAPPED));

// the following inside a loop that times out after a period
bret = ReadFile(gPipe, &tmostat, sizeof(TMO64STAT), NULL, &ol);
if (bret) break;
err = GetLastError();
// seeing err == ERROR_IO_PENDING or ERROR_PIPE_NOT_CONNECTED

所以我可以通过忽略错误并检查到达的数据来做我想做的事,但这让我很困扰。知道为什么我会出现这种行为吗?

【问题讨论】:

ERROR_PIPE_NOT_CONNECTED 是错误的,表示管道处于错误状态,ReadFile 完成并失败。 ERROR_IO_PENDING 表示ReadFile 表示操作开始但尚未完成。您只有在完成后才需要检查数据。忽略返回值/错误并检查数据是否错误 【参考方案1】:

Windows OVERLAPPED I/O 不像其他操作系统上的非阻塞标志那样工作(例如在 Linux 上,最接近的等价物是 aio_*() API,而不是 FIONBIO

使用 OVERLAPPED I/O,操作没有失败,它在后台继续。但你永远不会检查它......你只是再试一次。有一个待处理的操作队列,你总是开始新的,从不检查旧的。

填写 OVERLAPPED 结构中的hEvent 字段,并使用它来检测操作何时完成。然后调用GetOverlappedResult()获取实际传输的字节数。

另一个重要的注意事项 - 操作系统拥有 OVERLAPPED 结构和缓冲区,直到操作完成,您必须注意确保它们保持有效并且在您确认第一个操作之前不要释放它们或将它们用于任何其他操作完成。


请注意,Win32 管道有一个实际的非阻塞模式,但 Microsoft 强烈建议不要使用它:

为了与 Microsoft LAN Manager 2.0 版兼容,支持非阻塞等待模式。此模式不应用于使用命名管道实现重叠输入和输出 (I/O)。应该使用重叠 I/O,因为它可以在函数返回后在后台运行耗时的操作。

Named Pipe Type, Read, and Wait Modes

【讨论】:

使用hEventGetOverlappedResult 是三种方式之一。和最坏的。另一种方法是使用带有ReadFileEx 的 apc 完成。第三种方式 - 将 hFile 绑定到 iocp - 或通过 BindIoCompletionCallback 或 (vista+) CreateThreadpoolIo。或通过CreateIoCompletionPort拥有处理的iocp @RbMm:如果您需要同时处理数十个连接,那么肯定需要完成端口。但是,如果您有一个应用程序,其中通信不是主要目的,而您只需要一个远程接口来检查状态,那么完成端口会适得其反。永远不要在没有充分理由的情况下将线程引入程序 - 同步的开销会迅速吞噬完成端口的任何性能优势,然后是一些。 @RbMm:我个人偏爱 APC 方法,但引入 ReadFileEx 并不能回答如何使 ReadFile 工作的问题。 即使是单管 - BindIoCompletionCallback 也足够好和高效。 APC 也不错(需要警报等待)。为什么我说使用事件完成是 3 个变体中最差的 - 首先需要在每个 io 之前创建事件并在之后关闭(或重用)。在第二和主要 - 没有额外的背景。说我们得到了那个事件的信号。我们如何从中获得重叠指针?需要手动映射|绑定。如果 apc/iocp 我们被重叠回来,作为上下文指针。正是因为这样它更好地比较事件 即使对于单个连接足够好也有正确的上下文等,但不使用全局OVERLAPPED 等,甚至对于单个管道 - 我们可以同时有多个 io - 读取和写入数据。我个人的选择——iocp。有时apc完成。从来没有事件。但这是每个人都为自己选择的

以上是关于使用 ReadFile 异步读取管道的主要内容,如果未能解决你的问题,请参考以下文章

node同步读取readFileSync和异步读取readFile的区别

异步读取文件[重复]

打破 ReadFile() 阻塞 - 命名管道 (Windows API)

命名管道:ConnectNamedPipe 后的 ReadFile 返回 ERROR_BROKEN_PIPE

命名管道问题

模板引擎不关心内容之——art-template,碰见的同步与fs.readFile异步以及函数回调问题的描述,针对fs的readfille读取文件时,返回不了异步函数返回值的解决方法