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).
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):
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();
}
}
}
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();
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With