24

为简化起见,这是 NamedPipe SERVER 正在等待 NamedPipe CLIENT 写入管道的情况(使用 WriteFile())

阻塞的 Windows API 是 ReadFile()

服务器已创建启用阻塞的同步管道(无重叠 I/O)

客户端已连接,现在服务器正在等待一些数据。

在正常的事情流程中,客户端发送一些数据,服务器处理它,然后返回到 ReadFile() 以等待下一个数据块。

同时发生了一个事件(例如用户输入),NamedPipe SERVER 现在必须执行一些其他代码,而在 ReadFile() 阻塞时它不能执行这些代码。

此时我需要提一下,NamedPipe Client 不是我的应用程序,所以我无法控制它。我不能让它发送几个字节来解除对服务器的阻塞。它只是坐在那里不发送任何数据。由于我无法控制客户端实现,因此我无法对此进行任何更改。

一种解决方案是创建一个单独的线程,在其中执行所有 ReadFile() 操作。这样当事件发生时,我就可以处理代码。问题在于该事件还需要一个单独的线程,所以现在我为该服务器的每个实例添加了两个额外的线程。由于这需要可扩展,因此这是不可取的。

从另一个线程我尝试过调用

 DisconnectNamedPipe()

 CloseHandle()

它们都不会返回(直到客户端写入管道。)

我无法连接到同一个管道并写入几个字节,因为:

“命名管道的所有实例共享相同的管道名称,但每个实例都有自己的缓冲区和句柄,并为客户端/服务器通信提供单独的管道。”

http://msdn.microsoft.com/en-us/library/aa365590.aspx

我需要一种方法来伪造它,所以 64,000 美元的问题是:

如何打破 ReadFile() 的阻塞?

4

7 回答 7

18

在 ReadFile 之前试试这个:

BOOL WINAPI PeekNamedPipe(
  __in       HANDLE hNamedPipe,
  __out_opt  LPVOID lpBuffer,
  __in       DWORD nBufferSize,
  __out_opt  LPDWORD lpBytesRead,
  __out_opt  LPDWORD lpTotalBytesAvail,
  __out_opt  LPDWORD lpBytesLeftThisMessage
);

if(TotalBytesAvail > 0)
  ReadFile(....);

-AV-

于 2009-08-04T12:51:05.873 回答
6

看看 CancelSynchronousIo

将由指定线程发出的挂起的同步 I/O 操作标记为已取消。

和 CancelIo/CancelIoEx:

要取消所有挂起的异步 I/O 操作,请使用:

CancelIo — 此函数仅取消调用线程为指定文件句柄发出的操作。

CancelIoEx — 此函数取消线程为指定文件句柄发出的所有操作。

于 2009-02-27T03:01:42.433 回答
3

麦克风,

您无法取消同步 ReadFile。但是您可以切换到异步(重叠)操作。通过这样做,您可以实现一个非常可扩展的架构。

可能的算法(只是一个想法):

  • 对于每个新客户端调用 ReadFile
  • 句柄重叠的WaitForMultipleObjects.hEvent +您的自定义事件
  • 迭代信号事件,并安排它们由线程池中的线程执行。

这样你就可以只有很少的线程来接收连接和读取数据,而实际的数据处理可以由线程池完成。

于 2009-02-27T01:46:31.647 回答
2

问题在于该事件还需要一个单独的线程,所以现在我为该服务器的每个实例添加了两个额外的线程。由于这需要可扩展,因此这是不可取的。

在我的职业生涯中,我从未发现“更多线程”==“可扩展性更低”。你有多少这样的“服务器”实例?

通常,如果该操作将被阻塞并且系统需要在操作被阻塞时响应,则需要在单独的线程中执行该操作。

于 2009-02-27T01:14:31.380 回答
1

如果异步 I/O 操作使用 I/O 完成端口,则它们不必阻塞任何线程。请参阅:http: //msdn.microsoft.com/en-us/library/aa365198 (VS.85).aspx

于 2009-06-04T03:26:16.153 回答
1

发生的情况是,当您的客户端尝试连接到服务器入站管道(不再存在)时,服务器出站管道处于打开状态等待连接......您需要做的是清除出站管道以便循环回到您的入站。您可以通过读取文件在客户端刷新(记住循环连接建立,因为那里有一个“握手”,它永远不会第一次工作)

于 2017-03-23T18:57:29.460 回答
0

只需使用SetNamedPipeHandleState函数 https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate

调用此函数时使用PIPE_NOWAIT标志。

hNamedPipe应该是从CreateFile函数返回的句柄。

之后,ReadFile当没有可用数据时,调用不会阻塞线程。

于 2020-10-05T05:54:48.017 回答