Async NamedPipes in case of multiple pipe server i

2019-05-24 00:12发布

问题:

I'm using code from this article with only difference that maxNumberOfServerInstances is set to -1 (number of server instances with the same pipe name is limited only by system resources) in NamedPipeServerStream constructor

Async Listen Method [Listen Server Class]:

class PipeServer
{
string _pipeName;

public void Listen(string PipeName)
{
    try
    {
        // Set to class level var so we can re-use in the async callback method
        _pipeName = PipeName;
        // Create the new async pipe 
        NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeName, 
           PipeDirection.In, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

        // Wait for a connection
        pipeServer.BeginWaitForConnection
        (new AsyncCallback(WaitForConnectionCallBack), pipeServer);
    }
    catch (Exception oEX)
    {   ...   }
}

private void WaitForConnectionCallBack(IAsyncResult iar)
{
    try
    {
        // Get the pipe
        NamedPipeServerStream pipeServer = (NamedPipeServerStream)iar.AsyncState;
        // End waiting for the connection
        pipeServer.EndWaitForConnection(iar);

        // Read the incoming message
        byte[] buffer = new byte[255];           
        pipeServer.Read(buffer, 0, 255);

        // Convert byte buffer to string
        string stringData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
        ...

        // Kill original sever and create new wait server
        pipeServer.Close();
        pipeServer = null;
        pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 
           -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

        // Recursively wait for the connection again and again....
        pipeServer.BeginWaitForConnection(
           new AsyncCallback(WaitForConnectionCallBack), pipeServer);
    }
    catch
    { ... }
}
}

Async Send Method [PipeClient Class]

class PipeClient
{
public void Send(string SendStr, string PipeName, int TimeOut = 1000)
{
    try
    {
        NamedPipeClientStream pipeStream = new NamedPipeClientStream
           (".", PipeName, PipeDirection.Out, PipeOptions.Asynchronous);

        // The connect function will indefinitely wait for the pipe to become available
        // If that is not acceptable specify a maximum waiting time (in ms)
        pipeStream.Connect(TimeOut);            

        byte[] _buffer = Encoding.UTF8.GetBytes(SendStr);
        pipeStream.BeginWrite
        (_buffer, 0, _buffer.Length, new AsyncCallback(AsyncSend), pipeStream);
    }
    catch (TimeoutException oEX)
    {    ...        }
}

private void AsyncSend(IAsyncResult iar)
{
    try
    {
        // Get the pipe
        NamedPipeClientStream pipeStream = (NamedPipeClientStream)iar.AsyncState;

        // End the write
        pipeStream.EndWrite(iar);
        pipeStream.Flush();
        pipeStream.Close();
        pipeStream.Dispose();
    }
    catch (Exception oEX)
    {    ...         }
}
}

And I have two WinForms applications: server only have Listen button (clicking results in Listen method call), client has textBox for text input and Send button (clicking results in Send method call).

I do the following:

  1. start multiple copies of server application (as the result - multiple instances of NamedPipeServerStream with the same pipe name are created)
  2. click Listen button in each of them
  3. start client application
  4. click Send button

That results in receiving message by only one server application (that one, whose Listen button was clicked first). If I click Send button one more time - message is received by second-clicked server application. So instances receive messages in order of pressing Listen button on them and that repeats in cycle (but I'm not 100% sure that such order will be the same under all conditions).

Such behavior is strange for me: I've expected message to be received by all instances at the same time.

Could someone explain me why it is happening in such way?

How can I deliver message to all instaces with one Send button click?

回答1:

So finally I've found a solution (don't sure it's optimal - but it's working). It is based on using NamedPipeClientStream.NumberOfServerInstances.

public void Send(string SendStr, string PipeName, int TimeOut = 1000)
{
    try
    {
        NamedPipeClientStream pipeStream = new NamedPipeClientStream
          (".", PipeName, PipeDirection.Out, PipeOptions.Asynchronous);

        // The connect function will indefinitely wait for the pipe to become available
        // If that is not acceptable specify a maximum waiting time (in ms)
        pipeStream.Connect(TimeOut); 

        int _serverCount = pipeStream.NumberOfServerInstances; 

        byte[] _buffer = Encoding.UTF8.GetBytes(SendStr);
        pipeStream.BeginWrite(_buffer, 0, _buffer.Length, new AsyncCallback(AsyncSend), pipeStream);

        //there is more than 1 server present
        for (int i = 1; i < _serverCount; i++)
            {
                //create another client copy and use it
                NamedPipeClientStream pipeStream2 = new NamedPipeClientStream
                (".", PipeName, PipeDirection.Out, PipeOptions.Asynchronous);

                pipeStream2.Connect(TimeOut);

                byte[] buffer2 = Encoding.UTF8.GetBytes(SendStr);
                pipeStream2.BeginWrite(buffer2, 0, buffer2.Length, AsyncSend, pipeStream2);
            }
    }
    catch (TimeoutException oEX)
    {    ...        }
}

Note that this code doensn't handle situation when NumberOfServerInstances changes while cycle is running (sudden server instance closing, etc.)

BTW Don't have any idea why MSDN suggests to use

 // Kill original sever and create new wait server
 pipeServer.Close();
 pipeServer = null;
 pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 
    -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

 // Recursively wait for the connection again and again....
 pipeServer.BeginWaitForConnection(
   new AsyncCallback(WaitForConnectionCallBack), pipeServer);

Instead I've tested simply disconnecting current client

pipeServer.Disconnect();

// Recursively wait for the connection again and again....
pipeServer.BeginWaitForConnection(
   new AsyncCallback(WaitForConnectionCallBack), pipeServer);

and it worked for me the same way.