1

I'm working on a simple TCP server application using C# v4.0 (.Net Framework v4):

I want to accomplish these two steps:

  1. Client sends message1 to Server (client can be .net or java application)
  2. 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?

geek
  • 346
  • 6
  • 14
  • Does this [answer](http://stackoverflow.com/a/17216265/5781248) look useful? It mentions `ReceiveTimeout` property. – J.J. Hakala Jul 21 '16 at 10:40
  • @J.J.Hakala I tried it but it generate an exception without returning any received data. – geek Jul 21 '16 at 13:32

2 Answers2

2

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();
Mujahid Daud Khan
  • 1,983
  • 1
  • 14
  • 23
0

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.

J.J. Hakala
  • 6,136
  • 6
  • 27
  • 61