HavenDV / H.Pipes

A simple, easy to use, strongly-typed, async wrapper around .NET named pipes.
MIT License
219 stars 26 forks source link

The server stops working #27

Open Green7 opened 1 year ago

Green7 commented 1 year ago

Describe the bug

I am looking for a reliable named pipes server for communication between C ++ and C #. Unfortunately, the H.Pipes server can easily crash due to an error in the client. Here's an example code that shows how to make the server stop responding to new connections:

Steps to reproduce the bug

  1. Run ConsoleApp from samples as server
  2. Connect to server by the following code:
using System;
using System.IO.Pipes;
using System.Net;
using System.Threading;

namespace HPipeError
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      var pipeClient = new NamedPipeClientStream("named_pipe_test_server");
      while (!pipeClient.IsConnected)
      {
        pipeClient.Connect();
        Thread.Sleep(100);
      }
      // read string length
      var buffer = new byte[sizeof(int)];
      pipeClient.Read(buffer, 0, sizeof(int));
      var len = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0));
      // read string content
      buffer = new byte[len];
      pipeClient.Read(buffer, 0, len);
    }
  }
}
  1. After executing this code, try to connect to the server (e.g. using SampleApp as a client). It will be impossible.

Expected behavior

No response

Screenshots

No response

NuGet package version

No response

Platform

No response

IDE

No response

Additional context

Why is this happening?

After establishing the connection by the client, the server sends the name of the pipe created for the client through the main pipe. The server then waits for the client to establish a connection to the newly created pipe, but unfortunately the main pipe is closed. If the client fails to connect, the main pipe will not be recreated and you will not be able to connect to the server anymore. So one malfunctioning client can therefore block the server.

Green7 commented 1 year ago

Ok, I'm waiting.

Green7 commented 1 year ago

There is another problem with the current server code when establishing a connection. What is the situation like: First, I connect to the main pipe and read the client connection pipe name from it. Next I'm connecting to client connection pipe. But this connection is not successful, I have to retry it or wait a few ms before trying to establish it. Why is this happening? Because in the server code we have:

await handshakeWrapper.WriteAsync (Encoding.UTF8.GetBytes (connectionPipeName), token)

and then

// Wait for the client to connect to the data pipe
var connectionStream = CreatePipeStreamFunc? .Invoke (connectionPipeName) ?? PipeServerFactory.Create (connectionPipeName);

So we first send the client's connection name and then create a pipe with that name. If the client is fast (like C ++ code) it will read the name and try to connect before the server creates a pipe for client....

Green7 commented 1 year ago

I looked at the code and I think that a similar problem will also occur in the client. If we restart the server when the client wants to read the name of the pipe created for it exception will occur and ReconnectionTimer.Stop() is called. So the AutoReconnect mechanism will not work anymore. The solution to both problems seems to be moving the handshake code to PipeConnection class.

darklajid commented 11 months ago

I believe I ran into the same issue just now and can even kill the pipe (permanently, with no notice whatsoever) by accident. Is there a known workaround?

alexeygritsenko commented 11 months ago

@darklajid as a temporary you can use the server file with my fixes https://gist.github.com/alexeygritsenko/37499d4a5f36c31bf6db08e95675af8f

HavenDV commented 11 months ago

Thanks for bringing this up. I will take a look now

HavenDV commented 11 months ago

For now I added a test for this case and confirmed the issue.

darklajid commented 11 months ago

On top of the handshake issue (or maybe it's doing the same thing?) I also managed to kill the pipe without an error on the server side by running either accesschk or pipelist from the sysinternals suite (I ran both to figure out ACL issues and forgot which caused it: For one run the pipe is there, afterwards it's gone for good).