[c#] What is the correct way to read from NetworkStream in .NET

I've been struggling with this and can't find a reason why my code is failing to properly read from a TCP server I've also written. I'm using the TcpClient class and its GetStream() method but something is not working as expected. Either the operation blocks indefinitely (the last read operation doesn't timeout as expected), or the data is cropped (for some reason a Read operation returns 0 and exits the loop, perhaps the server is not responding fast enough). These are three attempts at implementing this function:

// this will break from the loop without getting the entire 4804 bytes from the server 
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        bytes = 0;
        if (stm.DataAvailable)
            bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

// this will block forever. It reads everything but freezes when data is exhausted
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

// inserting a sleep inside the loop will make everything work perfectly
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        Thread.Sleep(20);
        bytes = 0;
        if (stm.DataAvailable)
            bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

The last one "works", but it certainly looks ugly to put a hard-coded sleep inside the loop considering that sockets already support read timeouts! Do I need to setup some property(ies) on the TcpClient of the NetworkStream? Does the problem resides in the server? The server don't close the connections, it is up to the client to do so. The above is also running inside the UI thread context (test program), maybe it has something to do with that...

Does someone know how to properly use NetworkStream.Read to read data until no more data is available? I guess what I'm wishing for is something like the old Win32 winsock timeout properties... ReadTimeout, etc. It tries to read until the timeout is reached, and then return 0... But it sometimes seem to return 0 when data should be available (or on the way.. can Read return 0 if is available?) and it then blocks indefinitely on the last read when data is not available...

Yes, I'm at a loss!

This question is related to c# .net sockets tcpclient

The answer is


Networking code is notoriously difficult to write, test and debug.

You often have lots of things to consider such as:

  • what "endian" will you use for the data that is exchanged (Intel x86/x64 is based on little-endian) - systems that use big-endian can still read data that is in little-endian (and vice versa), but they have to rearrange the data. When documenting your "protocol" just make it clear which one you are using.

  • are there any "settings" that have been set on the sockets which can affect how the "stream" behaves (e.g. SO_LINGER) - you might need to turn certain ones on or off if your code is very sensitive

  • how does congestion in the real world which causes delays in the stream affect your reading/writing logic

If the "message" being exchanged between a client and server (in either direction) can vary in size then often you need to use a strategy in order for that "message" to be exchanged in a reliable manner (aka Protocol).

Here are several different ways to handle the exchange:

  • have the message size encoded in a header that precedes the data - this could simply be a "number" in the first 2/4/8 bytes sent (dependent on your max message size), or could be a more exotic "header"

  • use a special "end of message" marker (sentinel), with the real data encoded/escaped if there is the possibility of real data being confused with an "end of marker"

  • use a timeout....i.e. a certain period of receiving no bytes means there is no more data for the message - however, this can be error prone with short timeouts, which can easily be hit on congested streams.

  • have a "command" and "data" channel on separate "connections"....this is the approach the FTP protocol uses (the advantage is clear separation of data from commands...at the expense of a 2nd connection)

Each approach has its pros and cons for "correctness".

The code below uses the "timeout" method, as that seems to be the one you want.

See http://msdn.microsoft.com/en-us/library/bk6w7hs8.aspx. You can get access to the NetworkStream on the TCPClient so you can change the ReadTimeout.

string SendCmd(string cmd, string ip, int port)
{
  var client = new TcpClient(ip, port);
  var data = Encoding.GetEncoding(1252).GetBytes(cmd);
  var stm = client.GetStream();
  // Set a 250 millisecond timeout for reading (instead of Infinite the default)
  stm.ReadTimeout = 250;
  stm.Write(data, 0, data.Length);
  byte[] resp = new byte[2048];
  var memStream = new MemoryStream();
  int bytesread = stm.Read(resp, 0, resp.Length);
  while (bytesread > 0)
  {
      memStream.Write(resp, 0, bytesread);
      bytesread = stm.Read(resp, 0, resp.Length);
  }
  return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

As a footnote for other variations on this writing network code...when doing a Read where you want to avoid a "block", you can check the DataAvailable flag and then ONLY read what is in the buffer checking the .Length property e.g. stm.Read(resp, 0, stm.Length);


As per your requirement, Thread.Sleep is perfectly fine to use because you are not sure when the data will be available so you might need to wait for the data to become available. I have slightly changed the logic of your function this might help you little further.

string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();

    int bytes = 0;

    do
    {
        bytes = 0;
        while (!stm.DataAvailable)
            Thread.Sleep(20); // some delay
        bytes = stm.Read(resp, 0, resp.Length);
        memStream.Write(resp, 0, bytes);
    } 
    while (bytes > 0);

    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

Hope this helps!


Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to sockets

JS file gets a net::ERR_ABORTED 404 (Not Found) mysqld_safe Directory '/var/run/mysqld' for UNIX socket file don't exists WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400 TypeError: a bytes-like object is required, not 'str' Failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED No connection could be made because the target machine actively refused it 127.0.0.1 Sending a file over TCP sockets in Python socket connect() vs bind() java.net.SocketException: Connection reset by peer: socket write error When serving a file How do I use setsockopt(SO_REUSEADDR)?

Examples related to tcpclient

How to set the timeout for a TcpClient? What is the correct way to read from NetworkStream in .NET Connecting to TCP Socket from browser using javascript Sending and receiving data over a network using TcpClient In C#, how to check if a TCP port is available?