named pipe stream example not showing the result

2.4k Views Asked by At

I'm new to pipe stream and tried to practice. I've written the following two projects and I can't see the result (which I wrote within the server project) from the client project. Here's the first project:

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message))
{
    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");
    namedPipeServer.Write(bytes, 0, bytes.Length);
}

Here's the second project:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;
    string serverResponse = string.Empty;
    byte[] readBytes = new byte[5];
    while (!namedPipeClient.IsMessageComplete)
    {
        namedPipeClient.Read(readBytes, 0, readBytes.Length);
        serverResponse = Encoding.Default.GetString(readBytes);
        readBytes = new byte[5];
    }
    System.Console.WriteLine(serverResponse);
    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}

What's wrong with this?

Thanks

2

There are 2 best solutions below

3
On BEST ANSWER

The client doesn't receive any message from the server, because namedPipeClient.IsMessageComplete must be called after read operation. See PipeStream.IsMessageComplete on docs:

Gets a value indicating whether there is more data in the message returned from the most recent read operation.

Otherwise namedPipeClient.IsMessageComplete returns true and code inside while-loop is not executed. So you have to rewrite while loop to do-while loop to ensure, that read operation occurs before namedPipeClient.IsMessageComplete is tested.

But there are more problems, see comments for explanations:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;

    // StringBuilder is more efficient for string concatenation
    StringBuilder serverResponse = new StringBuilder();

    byte[] readBytes = new byte[5];

    do
    {
        // You need to store number of bytes read from pipe (to readCount variable).
        // It can be less then the length of readBytes buffer, in which case
        // GetString() would decode characters beyond end of message.
        var readCount = namedPipeClient.Read(readBytes, 0, readBytes.Length);
        var readText = Encoding.Default.GetString(readBytes, 0, readCount);

        // You original code "overwrites" content of serverResponse variable instead
        // of concatenating it to the previous value. So you would receive only 
        // the last part of the server message.
        serverResponse.Append(readText);

        // It is not needed to create new buffer, you can just reuse existing buffer
        //readBytes = new byte[5];

    // IsMessageComplete is now tested after read operation
    } while (!namedPipeClient.IsMessageComplete);

    System.Console.WriteLine(serverResponse.ToString());

    // You current server implementation exits as soon as it sends message to the client
    // and does not wait for incomming message. You'll have to change server accordingly 
    // to be able to send a message back to the server.
    //byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    //namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}

EDIT:

When named pipe is in the PipeTransmissionMode.Message mode, every call of NamedPipeServerStream.Write() on the server sends data through the pipe as an individual message. Client can then receive these messages separated from each other (as opposed to PipeTransmissionMode.Byte mode, where client receives just single continuous stream of bytes, no matter how many writes server performed using NamedPipeServerStream.Write()).

When client reads data from pipe (namedPipeClient.Read()), method is allowed to return less data then requested (for example when there is not enough space in the receiving buffer to store the whole message, or message is shorter then the requested number of bytes), see docs.

Returns the total number of bytes that are read into buffer. This might be less than the number of bytes requested if that number of bytes is not currently available, or 0 if the end of the stream is reached.

You can then use namedPipeClient.IsMessageComplete and readCount to detect this. Let me explain it on some example: Imagine that server sends message ABCDEFGHIJKL to the client, encoded to byte array as { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 }. This message has length of 12 bytes, thus it does not fit to your receiving buffer (readBytes) which is 5 bytes long. So when client reads from pipe for the first time using namedPipeClient.Read(), receiving buffer will contain just first 5 bytes ({ 65, 66, 67, 68, 69 } corresponding to ABCDE) of the message. And this is where namedPipeClient.IsMessageComplete will help us, because it will return false indicating that we didn't receive complete message, there are still some bytes left and we should continue reading.

The second read from the pipe will be similar, we will read second part of the message ({ 70, 71, 72, 73, 74 } corresponding to FGHIJ), namedPipeClient.IsMessageComplete is still false indicating incomplete message.

When third read from the pipe comletes, only 2 remaining bytes ({ 75, 76 } corresponding to KL) will be read, but our buffer is still 5 bytes long, so it will look like this: ({ 75, 76, 72, 73, 74 } corresponding to KLHIJ). Values 72, 73, 74 are still there from previous iteration of the loop. And now it is important to store value returned from namedPipeClient.Read() to the readCount variable. It will contain value 2, indicating that only 2 bytes of the bytesRead buffer are valid and the remaining bytes should be ignored.

0
On

Try to user StreamReader for reading messages from both pipes. Server:

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte))
{
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");

    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.Write(bytes, 0, bytes.Length);
    namedPipeServer.WaitForPipeDrain();

    var reader = new StreamReader(namedPipeServer);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);
}

Client:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();

    var reader = new StreamReader(namedPipeClient);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);

    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
    namedPipeClient.WaitForPipeDrain();
}