How to wait for response from NamedPipeServer? - c#

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):
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();
}
}
}

Related

TcpClient doesn't reveive all data

I have a "messenger" application that works like this:
Client Sends Message to Server -> Server forwards Message to rest of the clients -> Clients read the message.
The server always receives the messages, but the clients do not.
Code that forwards the message to all clients (Server/TcpListener):
public static void SendToAll(TcpClient sender, string message)
{
// Log the recieved message in the console
Console.WriteLine(message);
// Save the recieved message for new clients
messageLog += message + "\n";
byte[] msg = Encoding.ASCII.GetBytes(message);
foreach (TcpClient client in clients.Keys)
{
if (client == sender || clients[client] == "") continue;
if (!client.Connected)
{
clients.Remove(client);
continue;
}
NetworkStream stream = client.GetStream();
stream.Write(msg, 0, msg.Length);
}
}
Code that reads messages from the server (TcpClient):
byte[] buffer = new byte[1024];
while (true)
{
using (MemoryStream memoryStream = new MemoryStream())
{
Int32 bytes = 0;
do
{
bytes = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytes);
}
while (stream.DataAvailable);
Console.WriteLine(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
Server (image attached)
Client1 (image attached)
Client2 (image attached)
Found the answer.
The problem was probably caused because TCP is slow; not entirely finished with reading all the stream and already tasked with reading new data.
So I Switched
byte[] buffer = new byte[1024];
while (true)
{
using (MemoryStream memoryStream = new MemoryStream())
{
Int32 bytes = 0;
do
{
bytes = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytes);
}
while (stream.DataAvailable);
Console.WriteLine(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
to
// ... Main() {
while (true) StartRead();
// ... }
public static async void StartRead(NetworkStream stream)
{
byte[] buffer = new byte[1024];
Int32 bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length));
string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(message);
}

C# TCP Read All Data - Server Side Issues

I'm working on a simple TCP server application using C# v4.0 (.Net Framework v4):
I want to accomplish these two steps:
Client sends message1 to Server (client can be .net or java application)
Server sends back message2 to Client as a response to message1
I have a problem with my server, it is not able to read message1 correctly unless I use one of these inappropriate solutions:
1) Use a MemoryStream with a Buffer of only 1 byte (works but slow):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[1]; // works but slow in case of big messages
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
Example: if message1.Length == 12501 and I use a buffer of 1024 the NetworkStream.Read() loop reads only 2048 bytes of message1, I think NetworkStream.DataAvailable does not return the correct value!
2) Use a Thread.Sleep(1000) after reading from NetworkStream to Buffer (works but slow):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[8192];
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
Thread.Sleep(1000); // works but receiving gets slow
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
3) Use StreamReader.ReadToEnd() and close the client's socket after sending messages1 (works but server cannot response to client with message2):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream, true);
string message1 = streamReader.ReadToEnd(); // blocks until client close its socket
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
4) Loop with StreamReader.ReadLine() and close the client's socket
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream);
StringBuilder stringBuilder = new StringBuilder();
while (!streamReader.EndOfStream)
{
stringBuilder.AppendLine(streamReader.ReadLine()); // blocks until client close its socket
}
string message1 = stringBuilder.ToString();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
5) Prefix message1 with its length (works but requires the client to add extra bytes to the message and this will not work with existing java clients)
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
byte[] bufferMessageLength = new byte[4]; // sizeof(int)
networkStream.Read(bufferMessageLength, 0, bufferMessageLength.Length);
int messageLength = BitConverter.ToInt32(bufferMessageLength, 4);
byte[] bufferMessage = new byte[messageLength];
networkStream.Read(bufferMessage, 0, bufferMessage.Length);
memoryStream.Write(buffer, 0, bufferMessage.Length);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
Regarding to these issues, what is the best method to read all data from the client without using the above mentioned solutions?
instead of using networkStream.DataAvailable append the size of data at the start of your message. for example the length of your message is 12501 use first 4 bytes as message length.
First define a method to read data from buffer
public static void ReadStream(NetworkStream reader, byte[] data)
{
var offset = 0;
var remaining = data.Length;
while (remaining > 0)
{
var read = reader.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
and then read data from stream.
var bytesRead = 0;
var offset = 0;
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
var bufferMessageSize = new byte[4]; // int32
ReadStream(networkStream, bufferMessageSize);
var messageSize = BitConverter.ToInt32(bufferMessageSize, 4); // bytesToRead
var bufferMessage = new byte[messageSize];
ReadStream(networkStream, bufferMessage);
// Now Respond back Client here
// networkStream.Write();
If the communication is line oriented, then StreamReader.ReadLine() could be suitable.
ReadLine() Reads a line of characters from the current stream and returns the
data as a string.

Sending and receiving images via a socket

I have a C# desktop app. It connects to another PC on my network which is a UWP C# app.
I am trying to send an image or 2 to my listening socket and to test this I get the listening socket to send me the image back.
The trouble is that even though my server recieves all the bytes that were orginally sent the recieved image back to the client is not of the same size.
To make this even more weird is sometimes the returned bytes are correct and I get the whole image and when I attempt to send 2 images the 1st one is OK and the 2nd one is not.
Then it will/can revert back to no images being sent back correctly.
I think is maybe to do with the async/await parts bit I am not sure how.
This is my server code:
using (IInputStream input = args.Socket.InputStream)
{
byte[] data = new byte[BufferSize];
IBuffer buffer = data.AsBuffer();
uint dataRead = BufferSize;
while (dataRead == BufferSize)
{
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
requestInBytes.AddRange(data.Take((int) buffer.Length));
dataRead = buffer.Length;
}
}
var ct = requestInBytes.Count;
I then trip out the header info:
int counter = 0;
counter = requestCommand[0].Length;
counter = counter + requestCommand[1].Length;
counter = counter + requestCommand[2].Length;
counter = counter + requestCommand[3].Length;
counter = counter + requestCommand[4].Length;
counter = counter + requestCommand[5].Length;
counter = counter + 6;
Now I extract the image:
var imgBody = new byte[totalBytes.Length- counter];
System.Buffer.BlockCopy(totalBytes, counter, imgBody, 0, imgBody.Length);
byteArray = imgBody;
And send just the image back:
using (IOutputStream output = args.Socket.OutputStream)
{
using (Stream response = output.AsStreamForWrite())
{
MemoryStream stream = new MemoryStream(byteArray);
await response.WriteAsync(byteArray, 0, byteArray.Length);
await response.FlushAsync();
}
}
This is my client code:
StringBuilder sb = new StringBuilder();
foreach (var gallery in Shared.CurrentJobGallery)
{
try
{
sb.Clear();
sb.Append(GeneralTags.ACTION_ADD);
sb.Append(Shared.DLE);
sb.Append("GALLERY");
sb.Append(Shared.DLE);
sb.Append(Shared.CurrentClientId);
sb.Append(Shared.DLE);
sb.Append(gallery.Title);
sb.Append(Shared.DLE);
sb.Append(gallery.Description);
sb.Append(Shared.DLE);
sb.Append(jobRef);
sb.Append(Shared.DLE);
byte[] galleryHdr = Encoding.UTF8.GetBytes(sb.ToString());
byte[] byteArray = new byte[galleryHdr.Length + gallery.ImageData.Length];
Buffer.BlockCopy(galleryHdr, 0, byteArray, 0, galleryHdr.Length);
Buffer.BlockCopy(gallery.ImageData, 0, byteArray, galleryHdr.Length, gallery.ImageData.Length);
List<byte> requestInBytes2 = new List<byte>();
System.Diagnostics.Debug.WriteLine("SENT: " + gallery.ImageData.Length.ToString());
using (TcpClient clientSocket = new TcpClient())
{
await clientSocket.ConnectAsync(GeneralTags.RASPBERRY_PI_IP_ADDRESS, GeneralTags.RASPBERRY_PI_PORT);
using (NetworkStream serverStream = clientSocket.GetStream())
{
List<byte> requestInBytes = new List<byte>();
serverStream.Write(byteArray, 0, byteArray.Length);
serverStream.Flush();
int i;
Byte[] bytes = new Byte[1024];
do
{
i = serverStream.Read(bytes, 0, bytes.Length);
byte[] receivedBuffer = new byte[i];
Array.Copy(bytes, receivedBuffer, i);
requestInBytes2.AddRange(receivedBuffer);
} while (serverStream.DataAvailable);
}
}
using (MemoryStream ms = new MemoryStream())
{
System.Diagnostics.Debug.WriteLine("BACK: " + requestInBytes2.Count.ToString());
ms.Write(requestInBytes2.ToArray(), 0, requestInBytes2.ToArray().Length);
Shared.ViewImage(Image.FromStream(ms, true));
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
Your problem is that TCP sockets are based around streams, not packets. It's true that "on the wire" everything is a packet, but when you're using TCP, you have no control over how the data is split up into packets or is reassembled into a stream.
In particular, this line of code is incorrect:
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
According to the docs, you must use the buffer returned from ReadAsync. Also note that this buffer may be a partial image, and it's up to your code to detect that situation, read more if necessary, and append those blocks together. Also, the buffer may contain part of one image and part of the next image; it's also up to your code to detect that and handle it correctly.
For this reason, most TCP applications use some form of message framing (described in more detail on my blog). Note that getting this right is surprisingly hard.
I strongly recommend that you use SignalR instead of raw TCP sockets. SignalR handles message framing for you, and it is capable of self-hosting (i.e., it does not require ASP.NET).

Sending Binary File TcpClient - File Is Larger Than Source

To put my toe in the water of Network programming, I wrote a little Console App to send a png file to a server (another console app). The file being written by the server is slightly bigger than the source png file. And it will not open.
The code for the client app is:
private static void SendFile()
{
using (TcpClient tcpClient = new TcpClient("localhost", 6576))
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
//FileStream fileStream = File.Open(#"E:\carry on baggage.PNG", FileMode.Open);
byte[] dataToSend = File.ReadAllBytes(#"E:\carry on baggage.PNG");
networkStream.Write(dataToSend, 0, dataToSend.Length);
networkStream.Flush();
}
}
}
The code for the Server app is :
private static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(Listen));
thread.Start();
Console.WriteLine("Listening...");
Console.ReadLine();
}
private static void Listen()
{
IPAddress localAddress = IPAddress.Parse("127.0.0.1");
int port = 6576;
TcpListener tcpListener = new TcpListener(localAddress, port);
tcpListener.Start();
using (TcpClient tcpClient = tcpListener.AcceptTcpClient())
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
var data = new List<byte>();
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
var copy = new byte[length];
Array.Copy(bytes, 0, copy, 0, length);
data.AddRange(copy);
}
BinaryFormatter binaryFormatter = new BinaryFormatter();
stream.Position = 0;
binaryFormatter.Serialize(stream, data.ToArray());
}
}
}
tcpListener.Stop();
The size of the written file is 24,103Kb, whereas the source file is only 24,079Kb.
Is it apparent to anyone why this operation is failing?
Cheers
You are writing your output using a BinaryFormatter. I'm pretty sure that this will add some bytes at the start of the output to indicate the type that you're outputting (in this case System.Byte[]).
Just write the bytes out directly to the file without using the formatter:
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, length);
}
}

setting NetworkStream.ReceiveTimeout not triggering exception

This is a continuation of the this question. I am new to network programming, so I am just writing small sample stuff to gain understanding, but somewhat struggling with explaining results.
It seems setting NetworkStream.ReceiveTimeout is not working correctly when client that was supposed to be sending data simply closes before sending all the expected data.
Here is the sample code:
public static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 10001);
listener.Start();
ThreadPool.QueueUserWorkItem(WriterThread);
using (TcpClient client = listener.AcceptTcpClient())
using (NetworkStream stream = client.GetStream())
{
client.ReceiveTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
stream.ReadTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
ReceiveMessage(stream, 1024);
}
listener.Stop();
Console.WriteLine("Done.");
Console.ReadKey(true);
}
private static void WriterThread(object state)
{
using (TcpClient client = new TcpClient())
{
client.Connect(new IPEndPoint(IPAddress.Loopback, 10001));
using (NetworkStream stream = client.GetStream())
{
byte[] bytes = Encoding.ASCII.GetBytes("obviously less than 1024 bytes");
stream.Write(bytes, 0, bytes.Length);
Thread.Sleep(10000); // comment out
}
}
}
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
bufferFill += stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
This version works correctly triggering exception on the stream.Read() call. However If I comment out Thread.Sleep(10000), the client closes connection, but listener fails to recognize it. Main thread gets stuck inside the while(true) loop. The stream.Read() keeps returning zero, but no exception thrown.
Is this normal? If so how am I expected to handle abnormal client disconnections?
Yes, this sounds normal. There is no receive- or read timeout because the client has disconnected. This means that no more data is available for reading and the stream will return 0 immediately just as documented.
I would modify your ReceiveMessage method to something like the following:
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
int bytesRead = stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (bytesRead == 0)
throw new Exception("No more data available.");
bufferFill += bytesRead;
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
Clearly if the stream.Read() call returns 0 before we have received all the expected bytes there must have been some form of disconnection or similar. Either way we will never get any more data from the stream.
Edit: The Stream class has no notion of a "message". The Read method blocks until more data becomes available if none is already in the buffer. It will however return 0 when no more data can be received, which in this case means the connection is closed.

Categories

Resources