C# 异步命名管道永远等待

Posted

技术标签:

【中文标题】C# 异步命名管道永远等待【英文标题】:C# async named pipes wait for ever 【发布时间】:2016-10-04 13:29:28 【问题描述】:

我正在使用异步管道,并且在某些时候服务器和客户端都相互等待并且什么都不做。这是我的代码:

服务器:

var buffer = new byte[BufferSize];
using (var server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous))

    // Wait for client.
    Logger.Trace("Waiting for connection ...");
    var asyncResult = server.BeginWaitForConnection(a =>  , null);
     while (true)
     
          await Task.Delay(MillisecondsDelay);
          if (asyncResult.IsCompleted) break;
      
      server.EndWaitForConnection(asyncResult);
      Logger.Trace("Connection established.");


       using (var sr = new StreamReader(server))
       using (var sw = new StreamWriter(server))
       
             // Send work.
             Logger.Trace("Sending operations...");
             await sw.WriteAsync(JsonConvert.SerializeObject(data));
             await sw.FlushAsync();
             server.WaitForPipeDrain();

              // Wait for response.
              var bytesRead = await server.ReadAsync(buffer, 0, buffer.Length);

       .....

客户:

        // Read data from the pipe...
 using (var client = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous))
 
      Logger.Trace("Connecting to server...");
      client.Connect();
      Logger.Trace("Connected.");

      using (var sr = new StreamReader(client))
      using (var sw = new StreamWriter(client))
      
           // Read operations.
           Logger.Trace("Waiting operations...");
           var text = await sr.ReadToEndAsync();
       .....

我记录的日志如下:

2016-10-04 16:09:51.5375 => (TRACE) | MyController.Execute : Waiting for connection ... |  
2016-10-04 16:09:52.0706 => (TRACE) | MyController+<Execute>d__5.MoveNext : Connection established. |  
2016-10-04 16:09:52.0706 => (TRACE) | MyController+<Execute>d__5.MoveNext : Sending operations... |  

和:

2016-10-04 16:09:51.8486 => (TRACE) | EXE.Program.Process : Connecting to server... | 
2016-10-04 16:09:51.8696 => (TRACE) | EXE.Program.Process : Connected. | 
2016-10-04 16:09:51.8696 => (TRACE) | EXE.Program.Process : Waiting operations... | 

然后什么都没有!

之后似乎陷入僵局。

我认为我应该在服务器或客户端每次写入后使用WaitForPipeDrain(),我这样做了,但这似乎没有帮助。

客户端读取操作永远不会返回。

有什么想法吗?

【问题讨论】:

当你说ReadToEndAsync时,流阅读器怎么知道它在最后?如果使用ReadAsync,还会死锁吗? @DavidG 我的猜测是一个流结束(没有其他数据要读取),尽管流没有关闭。所以,我相信它可以从当前位置读取内容到当前流大小的末尾。而且我也刚刚尝试使用ReadLineAsync(),但仍然存在同样的问题。 ReadAsync 读取字节数组,因此需要更多重构... 好吧ReadLine 暗示有一个可能没有的行结尾。你也可以尝试在你的 awaitables 上使用ConfigureAwait(false) @DavidG 但是,我怎么知道我什么时候读取了所有服务器数据(ReadAsync 读取字节数组,我想当没有更多数据时也会阻塞,对吧)? ConfigureAwait(false) 会帮助性能吗?我在这里看到它可能没有多大帮助***.com/a/13494570/2173353。我没有 GUI,只有控制台应用和网站。 通常这样的通讯以特殊字符或字符序列终止,以便您知道何时到达终点。另一种方法是将长度作为前几个字节发送,这样您就可以准确地知道您期望的长度。 【参考方案1】:

我认为问题出在ReadToEndAsync 方法上。 StreamReader 无法确定传输是否结束。例如,它可能是一个缓慢的连接并且只发送了一半的数据。通常在这些情况下,您需要提出自己的“协议”来发送数据。方法有很多种,这里有几个:

    等待终止符。很多时候,我看到 ASCII 字符 4 用于此,因为它的意思是“EOT”(end of transmission)。但是,这是基于您的消息不仅仅是可能包含该字符的二进制数据流的假设。您可以将终止符改为一个序列,一些几乎永远不会出现在流中的字符。

    在消息前加上您要发送的数据长度。例如,前 4 个字节是始终的长度,抓取它之后,您现在就知道要读取多少了。

【讨论】:

以上是关于C# 异步命名管道永远等待的主要内容,如果未能解决你的问题,请参考以下文章

命名管道服务器,如何中断或超时等待客户端连接和传入数据

如何使用命名管道通信 C 和 C# 程序

C++ 使用命名管道

php包含来自fifo(命名管道)文件的指令

命名管道(C#)

如何写入命名管道而不等待读取管道