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
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);
}
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.