3

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?

Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
user2173353
  • 4,316
  • 4
  • 47
  • 79
  • 1
    When you say `ReadToEndAsync`, how does the stream reader know it's at the end? Does it still deadlock if you use `ReadAsync`? – DavidG Oct 04 '16 at 13:34
  • @DavidG My guess was that there *was* a stream end (where there is no other data to read), despite the fact that the stream is not closed. So, I believe that it would be possible for it to read the contents from the current position to the end of the current stream size. And I have also just tried using `ReadLineAsync()` instead, and it is still having the same issue. `ReadAsync` reads byte arrays, so that will require more refactoring... – user2173353 Oct 04 '16 at 13:41
  • Well `ReadLine` implies that there is a line ending which there might not be. You could also try using `ConfigureAwait(false)` on your awaitables. – DavidG Oct 04 '16 at 13:43
  • @DavidG But then, how can I know when I have read all the server data (`ReadAsync` reads byte arrays and I guess will block too when there is no more data, right)? Will `ConfigureAwait(false)` help performance? I see here that it might not help much http://stackoverflow.com/a/13494570/2173353. I don't have a GUI, only a console app and a web site. – user2173353 Oct 04 '16 at 13:55
  • I have tried reading a byte array out of the server data (and not till the end of the stream) and it works. But, I guess that, if I read more than what the server has sent it will `block` again. So, is there any reliable way to get the bytes remaining to be read? Is using the `sr.BaseStream.Length` and `sr.BaseStream.Position` to calculate what I want a good idea? I will try that now... Just it's hard to understand why there isn't a wrapper method for what I try to do. It's like reinventing the wheel... :( – user2173353 Oct 04 '16 at 14:04
  • I could always send the size in advance, now that I think of it... :^) – user2173353 Oct 04 '16 at 14:08
  • 1
    Generally comms like this are terminated by a special character or character sequence so you know when you have reached the end. Another method is sending the length as the first few bytes so you know exactly how much you are expecting. – DavidG Oct 04 '16 at 14:12
  • Thanks a lot! :) If you want, write a summary of these below, so that I can accept it as an answer! ;) – user2173353 Oct 04 '16 at 14:14

1 Answers1

4

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.

DavidG
  • 113,891
  • 12
  • 217
  • 223