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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/593175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:08:46  来源:igfitidea点击:

Breaking ReadFile() blocking - Named Pipe (Windows API)

c++cwinapinamed-pipes

提问by Mike Trader

To simplify, this is a situation where a NamedPipe SERVER is waiting for a NamedPipe CLIENT to write to the pipe (using WriteFile())

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

The Windows API that is blocking is ReadFile()

阻塞的 Windows API 是 ReadFile()

The Server has created the synchronous pipe (no overlapped I/O) with blocking enabled

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

The client has connected, and now the server is waiting for some data.

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

In the normal flow of things, the client sends some data and the server processes it and then returns to ReadFile() to wait for the next chunk of data.

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

Meanwhile an event occurs (user input for example) and the NamedPipe SERVER must now execute some other code, which it cannot do while the ReadFile() is blocking.

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

At this point I need to mention that the NamedPipe Client is not my application, so I have no control over it. I cannot make it send a few bytes to unblock the server. It is just going to sit there and send no data. Since I do not have control of the Client implementation I cannot change anything on that end.

在这一点上我需要提到 NamedPipe Client 不是我的应用程序,所以我无法控制它。我不能让它发送几个字节来解锁服务器。它只是坐在那里不发送数据。由于我无法控制客户端实现,因此我无法更改任何内容。

One solution would be to create a separate thread in which all ReadFile() operations are performed. That way when the event occurs, I can just process the code. The problem with that, is that the event also requires a separate thread, so now I have two additional threads for each instance of this server. Since this needs to be scalable, this is undesirable.

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

From another thread I have tried calling

从另一个线程我尝试调用

 DisconnectNamedPipe()

and

 CloseHandle()

they both will not return (until the client writes to the pipe.)

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

I cannot connect to the same pipe and write a few bytes because:

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

"All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles, and provides a separate conduit for client/server communication."

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

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

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

I need a way to fake it out, So the $64k dollar question is:

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

How can I break the blocking of ReadFile()?

如何打破 ReadFile() 的阻塞?

回答by Mike Trader

Try this before ReadFile :

在 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-

-AV-

回答by alex2k8

Take a look on CancelSynchronousIo

看看 CancelSynchronousIo

Marks pending synchronous I/O operations that are issued by the specified thread as canceled.

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

And CancelIo/CancelIoEx:

和 CancelIo/CancelIoEx:

To cancel all pending asynchronous I/O operations, use either:

CancelIo — this function only cancels operations issued by the calling thread for the specified file handle.

CancelIoEx — this function cancels all operations issued by the threads for the specified file handle.

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

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

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

回答by alex2k8

Mike,

麦克风,

You can't cancel synchronous ReadFile. But you can switch to asynchronous (overlapped) operations. By doing this, you can implement a pretty scalable architecture.

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

Possible algorithm (just an idea):

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

  • For each new client call ReadFile
  • WaitForMultipleObjects where the handles are overlapped.hEvent + your custom events
  • Iterate over signalled events, and schedule them for execution by threads from a threads pool.
  • 对于每个新客户端调用 ReadFile
  • 句柄重叠的 WaitForMultipleObjects.hEvent + 您的自定义事件
  • 迭代信号事件,并安排它们由线程池中的线程执行。

This way you can have only few threads to receive connections and read data, while the actual data processing can be done by the threads pool.

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

回答by Kevin

The problem with that, is that the event also requires a separate thread, so now I have two additional threads for each instance of this server. Since this needs to be scalable, this is undesirable.

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

Never in my career have I found that "more threads" == "less scalable". How many of these "server" instances do you have?

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

Normally, an operation needs to be performed in a separate thread if that operation is going to block and the system needs to be responsive while the operation is blocked.

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

回答by Kevin

Asynchronous I/O operations do not have to block any thread if they use I/O Completion Ports. See: http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx

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

回答by user3498796

What happening is the server outbound pipe is left open waiting for connection while your client is trying to connect to the server inbound pipe (which is no longer existent)... What you need to do is flush out your outbound pipe in order to loop back to your inbound. You can flush out on the client side by reading the file (remember to loop the connect establishment because there is a "handshake" in there, and it will never work the first time)

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