user2173353 user2173353 - 2 months ago 47
C# Question

C# async named pipes wait for ever

I am using asynchronous pipes and at some point both the server and the client wait for each other and do nothing. Here is my code:

Server:



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);

.....


Client:



// 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();
.....


The logs that I take are these:

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


and :

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


And then nothing!

After that it seems like deadlocked.

I think I should use
WaitForPipeDrain()
after each write by the server or the client and I do that, but it doesn't seem to help.

The client read operation never returns.

Any ideas anyone?

Answer

I think the problem is in the ReadToEndAsync method. There is no way for the StreamReader to determine the transmission is over. For example, it could be a slow connection and has only sent half the data. Generally in these situations you need to come up with your own "protocol" to send data. There are various methods, here's a couple:

  1. Wait for a terminator character. Many times I've seen ASCII character 4 used for this as it mean "EOT" (end of transmission). However, this is based on the assumption that your message isn't just a stream of binary data which may also contain that character. You can make the terminator a sequence instead, a few characters that are almost never going to appear in the stream.

  2. Prefix the message with the length of the data you are about to send. For example, the first 4 bytes are always the length, after grabbing that, you now know how much to read.