Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for response from NamedPipeServer?

Tags:

c#

named-pipes

I have the following client code, that I write stuff to my Server pipe, I was able to read it on the Server side but before I could reply back, the client would already try to read the still empty pipe. How do you wait on the NamedPipeClientStream?

using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", pipename, PipeDirection.InOut))
{
    pipe.Connect(5000);
    pipe.ReadMode = PipeTransmissionMode.Byte;

    byte[] ba = Encoding.Default.GetBytes("hello world");
    pipe.Write(ba, 0, ba.Length);

    var result = await Task.Run(() => {
        // this would return as soon as Server finished reading
        // but then server hasn't wrote anything back yet
        pipe.WaitForPipeDrain();

        // sample code on how i am planning to read, not tested,
        // since the pipe is still empty at this point
        using (StreamReader reader = new StreamReader(pipe))
        using (MemoryStream ms = new MemoryStream())
        {
            reader.BaseStream.CopyTo(ms);
            return Encoding.Default.GetString(ms.ToArray());
        }
    });

    return result;
}

I don't think I should be using WaitForPipeDrain but then there are no other options for my to wait, or to know when is ready to read? There are many examples out there, but none of them shows the proper way for the client to wait for a response.

The example shown from Microsoft seems to be using ReadLine() and leveraging EOL character when you send string data, but I'm dealing with byte[] data ("hello world" is just to get some bytes).

like image 712
codenamezero Avatar asked Oct 20 '25 12:10

codenamezero


1 Answers

You don't need to wait for data. NamedPipeClientStream represents a stream of bytes (it derives from System.IO.Stream) and if no data is currently available, reading from pipe (or from StreamReader that wraps that pipe) will simply block until data arrives.

For transfering textual data, reading with StreamReader.ReadLine() and writing with StreamWriter.WriteLine() will work fine. To transfer binary data, you can either encode binary data into textual form (for example using base64 encoding) and keep using StreamReader.ReadLine() / StreamWriter.WriteLine(). Or you can set server and client pipes into PipeStream.TransmissionMode to Message mode, and transfer each byte array as a single message, as follows (error checks omitted for brevity):

Client:

class Client
{
    static async Task Main(string[] args)
    {
        using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut))
        {
            pipe.Connect(5000);
            pipe.ReadMode = PipeTransmissionMode.Message;

            byte[] ba = Encoding.Default.GetBytes("hello world");
            pipe.Write(ba, 0, ba.Length);

            var result = await Task.Run(() => {
                return ReadMessage(pipe);
            });

            Console.WriteLine("Response received from server: " + Encoding.UTF8.GetString(result));
            Console.ReadLine();
        }
    }

    private static byte[] ReadMessage(PipeStream pipe)
    {
        byte[] buffer = new byte[1024];
        using (var ms = new MemoryStream())
        {
            do
            {
                var readBytes = pipe.Read(buffer, 0, buffer.Length);
                ms.Write(buffer, 0, readBytes);
            }
            while (!pipe.IsMessageComplete);

            return ms.ToArray();
        }
    }
}

Server:

class Server
{
    static void Main(string[] args)
    {
        using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(
            "testpipe",
            PipeDirection.InOut,
            NamedPipeServerStream.MaxAllowedServerInstances,
            PipeTransmissionMode.Message))//Set TransmissionMode to Message
        {
            // Wait for a client to connect
            Console.Write("Waiting for client connection...");
            pipeServer.WaitForConnection();

            Console.WriteLine("Client connected.");

            //receive message from client
            var messageBytes = ReadMessage(pipeServer);
            Console.WriteLine("Message received from client: " + Encoding.UTF8.GetString(messageBytes));

            //prepare some response
            var response = Encoding.UTF8.GetBytes("Hallo from server!");

            //send response to a client
            pipeServer.Write(response, 0, response.Length);

            Console.ReadLine();
        }
    }

    private static byte[] ReadMessage(PipeStream pipe)
    {
        byte[] buffer = new byte[1024];
        using (var ms = new MemoryStream())
        {
            do
            {
                var readBytes = pipe.Read(buffer, 0, buffer.Length);
                ms.Write(buffer, 0, readBytes);
            }
            while (!pipe.IsMessageComplete);

            return ms.ToArray();
        }
    }
}
like image 148
Ňuf Avatar answered Oct 23 '25 03:10

Ňuf