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# 异步命名管道永远等待的主要内容,如果未能解决你的问题,请参考以下文章