【发布时间】:2016-03-06 15:49:19
【问题描述】:
我正在为 Windows 编写一个简单的命名管道服务器,调用 Windows API(在 Java 中使用 JNA,但这不相关)。
我试图弄清楚如何避免服务器永远卡住等待客户端连接或来自客户端的数据。
服务器代码执行以下操作:
1) 它通过调用CreateNamedPipe 创建管道,在dwPipeMode 参数中使用PIPE_WAIT。
2) 它调用ConnectNamedPipe,直到客户端连接后才会返回。
3) 它进入一个循环,它通过调用ReadFile 从客户端重复读取一条消息,直到读取数据才返回,并且对于每条接收到的消息,它通过调用将一条消息发送回客户端作为响应WriteFile.
4) 在多次这样的对话之后,客户端和服务器将断开管道。
我只是希望能够在第 2 步等待 ConnectNamedPipe 和在第 3 步等待 ReadFile 时设置超时,但我看不到在哪里设置超时。 CreateNamedPipe 函数中有 nDefaultTimeOut 参数,但听起来并不适合这样做; API 文档说:
默认超时值,以毫秒为单位,如果WaitNamedPipe 函数指定NMPWAIT_USE_DEFAULT_WAIT。
所以CreateNamedPipe 中的nDefaultTimeOut arg 听起来像是客户端 将连接到管道的默认超时将用于他们的操作,并且只有当他们调用WaitNamedPipe 函数时。事实上,在我的测试中,值 0 或 1000 并没有什么区别,对 ConnectNamedPipe 的调用永远不会返回(除非客户端连接)。我正在寻找的是服务器超时,而不是调用ConnectNamedPipe 和ReadFile。
作为CreateNamedPipe 的文档,对于带有PIPE_WAIT 的dwPipeMode 参数说,Blocking mode is enabled. When the pipe handle is specified in the ReadFile, WriteFile, or ConnectNamedPipe function, the operations are not completed until there is data to read, all data is written, or a client is connected. Use of this mode can mean waiting indefinitely in some situations for a client process to perform an action.
因此,实现此类超时的方法可能是以非阻塞模式创建管道(使用PIPE_NOWAIT 而不是PIPE_WAIT),以便对ReadFile、WriteFile 和ConnectNamedPipe 的调用立即返回,然后以某种方式在循环中监控自己的事件(客户端连接或接收数据),并在循环中检查自己是否超时或发生了另一个中断事件(如用户单击取消按钮)?
添加:对于ReadFile 调用,我可以使用立即返回的PeekNamedPipe 检查是否有要读取的数据,然后才调用ReadFile .我会试试的。但是我对ConnectNamedPipe的调用仍然有同样的问题。
添加:正如我所怀疑和答案所证实的那样,作为管道的新手,我从某种偏斜的角度看待它们,从这个角度来看,对超时的需求似乎比实际更大。
F.ex.想要超时调用ReadFile 背后的原因是,如果我(服务器)在它里面从客户端读取数据并且客户端突然关闭,有时我可能最终会卡在ReadFile 中。但现在我知道如果ReadFile 正在从管道读取并且客户端关闭,ReadFile 将总是出错,因此执行不会卡在其中。
【问题讨论】:
-
正如 Melis 所建议的,异步 I/O 是正确的方法。请特别注意 PIPE_NOWAIT 仅用于向后兼容,通常不应在新代码中使用。更重要的是,超时 ConnectNamedPipe 有点不寻常:你确定这是你想要做的吗?除非您知道应该始终至少有一个客户端尝试连接,否则这没有任何意义。
-
@HarryJohnston 我以前从未使用过命名管道,所以我不太确定。但是我在想,当没有客户端尝试连接时,执行将停留在 ConnectNamedPipe 中,如果此时服务器决定关闭管道并关闭,它需要 ConnectNamedPipe 返回,以便至少调用 CloseHandle。此外,如果 ConnectNamedPipe 没有返回,我无法检查 f.ex。如果用户按下了 Esc 键或单击了取消按钮。也许我没有以正确的方式使用 ConnectNamedPipe ?顺便说一句,在我的用例中,大多数时候没有客户端尝试连接。
-
好吧,需要监听某种消息和需要超时并不是一回事,这就是我有点困惑的原因。但它们是异步 I/O 有用的情况的完美示例! (还有一个替代方案,您可以有多个线程,但异步 I/O 更有效。)在这种情况下,您可能希望使用 MsgWaitForMultipleObjects() 来避免轮询。
-
@HarryJohnston 我不遵循,很可能我的设计(我的 Q 中的 4 点)不正确。我确实需要等待客户端连接,但我还需要用户可以中断这种等待,如果
ConnectNamedPipe在客户端连接之前永远不会返回,我不知道该怎么做。我以为我会让ConnectNamedPipe每隔几秒就超时,检查用户是否要求终止,如果没有,请再次调用ConnectNamedPipe并重复。但我认为我偏离了轨道,我会调查MsgWaitForMultipleObjects,也感谢您指出PIPE_NOWAIT已被弃用。 -
如果 ConnectNamedPipe 在客户端连接之前永远不会返回,我不知道该怎么做 - 如果您使管道异步,ConnectNamedPipe(和 ReadFile,就此而言)将总是立即返回。操作将在后台继续,完成后将设置事件对象。当您注意到发生这种情况时,您调用 GetOverlappedResult 以查看操作是否成功。
标签: windows winapi timeout named-pipes