Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# / .NET Keep named pipe open and support any number of parallel connecting clients

We have multiple clients connect to the server over named pipes. Occasionally we are seeing a pipe/file not found error when the client tries to connect to a named pipe. Our original code is

    while (!cancelToken.IsCancellationRequested)
    {
        // This creates a new NamedPipeServerStream
        var stream = PipeHelper.NewCurrentUserServerStream(PipeNames.Server, PipeTransmissionMode.Byte);
        await stream.WaitForConnectionAsync(cancelToken);

        Task.Run(() => HandleStreamAsync(stream).WithExceptionTrapper(LogNamedPipeException));

From what I can tell, a NamedPipeServerStream should be created and then WaitForConnection should be called, then the server should handle the request.

However, if that request is near instantaneous, or the client shuts down the pipe, it seems possible that the server stream will be closed and the whole named pipe will disappear, before the code loops around and a new server stream is created.

So how do we ensure the named pipe exists for the lifetime of the server, regardless of timing and number of connecting clients?

The code here https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication suggests that simply using a number of threads is an option (4, in that example). However this seems to only reduce the probability of error and its possible to have a client try to connect in a rare gap where all 4 threads have just closed a server stream and the named pipe disappears. In addition, it limits the parallel execution to 4 threads.

Is there a mechanism to allow us to open a named pipe server which allows arbitrary numbers of connections, creates a new server stream for every connection and never risks closing the named pipe?

Right now we are thinking about doing this:

    var nextStream = PipeHelper.NewCurrentUserServerStream(PipeNames.Server, PipeTransmissionMode.Byte);
    while (!cancelToken.IsCancellationRequested)
    {
        try
        {
            await nextStream.WaitForConnectionAsync(cancelToken);
            var currentStream = nextStream;
            // Always have another stream ready so if the task is handled and finished 
            // really quickly theres no chance of the named pipe being removed altogether.
            nextStream = PipeHelper.NewCurrentUserServerStream(PipeNames.Server, PipeTransmissionMode.Byte);

            Task.Run(() => HandleStreamAsync(currentStream).WithExceptionTrapper(LogNamedPipeException));
        }

This basically ensures we create a 2nd server stream before we start handling the 1st server stream connection, so we're guaranteed there is always one active, and we're running the handler on a separate task so we should start another WaitForConnection as soon as possible.

Is there a framework pattern that allows us to do the above, supporting any number of parallel clients and ensuring the named pipe is never removed provided the server is still active?

like image 625
Michael Parker Avatar asked Nov 07 '22 19:11

Michael Parker


1 Answers

So how do we ensure the named pipe always exists regardless of timing and number of connecting clients?

This isn't possible for any kind of network server. Even TCP/IP (on which named pipes are built) has a limited "backlog". Even if you modify the backlog to be a ridiculous amount, you'll still be limited by server hardware.

What you can do is always keep a certain number of listening calls (i.e., WaitForConnectionAsync), so you always have a good number of listeners going at all times, starting a new listen as soon as each one completes, just like your last code example. You'll want to run that code several times, like 20 or so, so you have lots of listeners. All you can do is minimize the chance of missing a connection; you can't prevent it completely.

like image 60
Stephen Cleary Avatar answered Nov 15 '22 10:11

Stephen Cleary