Can a TCP c# client receive and send continuously/consecutively without sleep?

50,293

There's nothing wrong necessarily with grouping questions together, but it does make answering the question more challenging... :)

The MSDN article you linked shows how to do a one-and-done TCP communication, that is, one send and one receive. You'll also notice it uses the Socket class directly where most people, including myself, will suggest using the TcpClient class instead. You can always get the underlying Socket via the Client property should you need to configure a certain socket for example (e.g., SetSocketOption()).

The other aspect about the example to note is that while it uses threads to execute the AsyncCallback delegates for both BeginSend() and BeginReceive(), it is essentially a single-threaded example because of how the ManualResetEvent objects are used. For repeated exchange between a client and server, this is not what you want.

Alright, so you want to use TcpClient. Connecting to the server (e.g., TcpListener) should be straightforward - use Connect() if you want a blocking operation or BeginConnect() if you want a non-blocking operation. Once the connection is establish, use the GetStream() method to get the NetworkStream object to use for reading and writing. Use the Read()/Write() operations for blocking I/O and the BeginRead()/BeginWrite() operations for non-blocking I/O. Note that the BeginRead() and BeginWrite() use the same AsyncCallback mechanism employed by the BeginReceive() and BeginSend() methods of the Socket class.

One of the key things to note at this point is this little blurb in the MSDN documentation for NetworkStream:

Read and write operations can be performed simultaneously on an instance of the NetworkStream class without the need for synchronization. As long as there is one unique thread for the write operations and one unique thread for the read operations, there will be no cross-interference between read and write threads and no synchronization is required.

In short, because you plan to read and write from the same TcpClient instance, you'll need two threads for doing this. Using separate threads will ensure that no data is lost while receiving data at the same time someone is trying to send. The way I've approached this in my projects is to create a top-level object, say Client, that wraps the TcpClient and its underlying NetworkStream. This class also creates and manages two Thread objects, passing the NetworkStream object to each during construction. The first thread is the Sender thread. Anyone wanting to send data does so via a public SendData() method on the Client, which routes the data to the Sender for transmission. The second thread is the Receiver thread. This thread publishes all received data to interested parties via a public event exposed by the Client. It looks something like this:

Client.cs

public sealed partial class Client : IDisposable
{
    // Called by producers to send data over the socket.
    public void SendData(byte[] data)
    {
        _sender.SendData(data);
    }

    // Consumers register to receive data.
    public event EventHandler<DataReceivedEventArgs> DataReceived;

    public Client()
    {
        _client = new TcpClient(...);
        _stream = _client.GetStream();

        _receiver = new Receiver(_stream);
        _sender   = new Sender(_stream);

        _receiver.DataReceived += OnDataReceived;
    }

    private void OnDataReceived(object sender, DataReceivedEventArgs e)
    {
        var handler = DataReceived;
        if (handler != null) DataReceived(this, e);  // re-raise event
    }

    private TcpClient     _client;
    private NetworkStream _stream;
    private Receiver      _receiver;
    private Sender        _sender;
}


Client.Receiver.cs

private sealed partial class Client
{
    private sealed class Receiver
    {
        internal event EventHandler<DataReceivedEventArgs> DataReceived;

        internal Receiver(NetworkStream stream)
        {
            _stream = stream;
            _thread = new Thread(Run);
            _thread.Start();
        }

        private void Run()
        {
            // main thread loop for receiving data...
        }

        private NetworkStream _stream;
        private Thread        _thread;
    }
}


Client.Sender.cs

private sealed partial class Client
{
    private sealed class Sender
    {
        internal void SendData(byte[] data)
        {
            // transition the data to the thread and send it...
        }

        internal Sender(NetworkStream stream)
        {
            _stream = stream;
            _thread = new Thread(Run);
            _thread.Start();
        }

        private void Run()
        {
            // main thread loop for sending data...
        }

        private NetworkStream _stream;
        private Thread        _thread;
    }
}

Notice that these are three separate .cs files but define different aspects of the same Client class. I use the Visual Studio trick described here to nest the respective Receiver and Sender files under the Client file. In a nutshell, that's the way I do it.

Regarding the NetworkStream.DataAvailable/Thread.Sleep() question. I would agree that an event would be nice, but you can effectively achieve this by using the Read() method in combination with an infinite ReadTimeout. This will have no adverse impact on the rest of your application (e.g., UI) since it's running in its own thread. However, this complicates shutting down the thread (e.g., when the application closes), so you'd probably want to use something more reasonable, say 10 milliseconds. But then you're back to polling, which is what we're trying to avoid in the first place. Here's how I do it, with comments for explanation:

private sealed class Receiver
{
    private void Run()
    {
        try
        {
            // ShutdownEvent is a ManualResetEvent signaled by
            // Client when its time to close the socket.
            while (!ShutdownEvent.WaitOne(0))
            {
                try
                {
                    // We could use the ReadTimeout property and let Read()
                    // block.  However, if no data is received prior to the
                    // timeout period expiring, an IOException occurs.
                    // While this can be handled, it leads to problems when
                    // debugging if we are wanting to break when exceptions
                    // are thrown (unless we explicitly ignore IOException,
                    // which I always forget to do).
                    if (!_stream.DataAvailable)
                    {
                        // Give up the remaining time slice.
                        Thread.Sleep(1);
                    }
                    else if (_stream.Read(_data, 0, _data.Length) > 0)
                    {
                        // Raise the DataReceived event w/ data...
                    }
                    else
                    {
                        // The connection has closed gracefully, so stop the
                        // thread.
                        ShutdownEvent.Set();
                    }
                }
                catch (IOException ex)
                {
                    // Handle the exception...
                }
            }
        }
        catch (Exception ex)
        {
            // Handle the exception...
        }
        finally
        {
            _stream.Close();
        }
    }
}

As far as 'keepalives' are concerned, there is unfortunately not a way around the problem of knowing when the other side has exited the connection silently except to try sending some data. In my case, since I control both the sending and receiving sides, I've added a tiny KeepAlive message (8 bytes) to my protocol. This is sent every five seconds from both sides of the TCP connection unless other data is already being sent.

I think I've addressed all the facets that you touched on. I hope you find this helpful.

Share:
50,293
commentator8
Author by

commentator8

Updated on March 30, 2020

Comments

  • commentator8
    commentator8 about 4 years

    This is to a degree a "basics of TCP" question, yet at the same time I have yet to find a convincing answer elsewhere and believe i have a ok/good understanding of the basics of TCP. I am not sure if the combination of questions (or the one questions and while i'm at it the request for confirmation of a couple of points) is against the rules. Hope not.

    I am trying to write a C# implementation of a TCP client, that communicates with an existing app containing a TCP server (I don't have access to its code, so no WCF). How do I connect to it, send and receive as needed as new info comes in or out, and ultimately disconnect. Using the following MSDN code as an example where they list "Send" and "Receive" asynchronous methods (or just TcpClient), and ignoring the connect and disconnect as trivial, how can I best go about continuously checking for new packets received and at the same time send when needed?

    I initially used TCPClient and GetStream(), and the msdn code still seems to require the loop and sleep described in a bit (counter intuitively), where I run the receive method in a loop in a separate thread with a sleep(10) milliseconds, and Send in the main (or third) thread as needed. This allows me to send fine, and the receive method effectively polls at regular intervals to find new packets. The received packets are then added to a queue.

    Is this really the best solution? Shouldn't there be a DataAvailable event equivalent (or something i'm missing in the msdn code) that allows us to receive when, and only when, there is new data available?

    As an afterthought I noticed that the socket could be cut from the other side without the client becoming aware till the next botched send. To clarify then, the client is obliged to send regular keepalives (and receive isn't sufficient, only send) to determine if the socket is still alive. And the frequency of the keepalive determines how soon I will know that link is down. Is that correct? I tried Poll, socket.connected etc only to discover why each just doesn't help.

    Lastly, to confirm (i believe not but good to make sure), in the above scenario of sending on demand and receiving if tcpclient.DataAvailable every ten seconds, can there be data loss if sending and receiving at the same time? If at the same time I am receiving I try and send will one fail, overwrite the other or any other such unwanted behaviour?

  • commentator8
    commentator8 over 10 years
    This was a very impressive answer. Feel like it should be framed (-: Thanks for taking the time to lay out a detailed, comprehensive and comprehensible explanation including examples!
  • BVB
    BVB almost 10 years
    Thank you very much for putting the time into such a wonderful answer!
  • Matt Davis
    Matt Davis almost 10 years
    @BVB, thanks for the nice comments. I glad you both found it helpful. Take care.
  • Andez
    Andez almost 10 years
    Me too thanks a lot. I couldn't fathom using this in the Async scenario without the memory leaks causing my apps memory usage to grow and grow here stackoverflow.com/questions/24268434/…
  • Ravior
    Ravior over 7 years
    The only thing wrong with this answer is, that the author doesnt think that its appropiate putting his solution in a zip file to let us download it instead of copy pasting his code.
  • Peter Duniho
    Peter Duniho almost 7 years
    I find it unfortunate that a network example that uses polling, including DataAvailable, dedicating threads to I/O, and using explicit keep-alive messages in the protocol rather than using the keep-alive socket option, is one of the higher-voted examples on Stack Overflow. Good network code would eschew all of these techniques, and use instead the highly-scalable asynchronous APIs made available in .NET. Especially in modern C# with async/await, where doing so is made that much easier (though it was always worthwhile and superior to the approach shown here).
  • Matt Davis
    Matt Davis almost 7 years
    @PeterDuniho lol. So instead of taking the time to provide an alternate, perhaps better, example in your own answer, which is what SO is all about, you instead feel compelled to denigrate this answer which has been around 3.5 years. I'll challenge you to do the former so that we can all learn how it should be done.
  • Peter Duniho
    Peter Duniho almost 7 years
    There are, actually, many good examples of the right way to write network code in .NET. I can point out what's wrong with this one without any obligation to provide an alternative. But I did in fact just a week or so ago post an example that shows the right way to deal with asynchronous I/O here on Stack Overflow: stackoverflow.com/questions/44924744/…. No challenge needed.
  • Matt Davis
    Matt Davis almost 7 years
    I congratulate you on your recent SO answer as it is indeed instructive. To my knowledge, it is the only example I've seen that shows how to both read and write from the same socket using the async/await paradigm (although I haven't looked in a while). Again, kudos. I still think it would be worthwhile to reproduce the latter part of your answer here for future visitors. And perhaps in the future you will consider providing such a link/answer to begin with instead of just poking holes in answers that work differently from the way you'd do it.
  • Matt Davis
    Matt Davis almost 7 years
    blog.stephencleary.com/2009/05/… Be aware of the disadvantages of using TCP/IP keepalives. See solution #4.
  • Sturla
    Sturla about 5 years
    I created a GitHub repo where I implemented this code but I´m having an issue with some threading when I use Winforms. Anybody interested to look at my bug issue at github?
  • Sturla
    Sturla about 5 years
    Hi @MattDavis I'm kinda stuck with my example. It works with client/server console programs but when I go into WinForms (or even Xamarin Forms) it does not. Some UI threading probably that I just cant figure out. Any change you want to take a look for me? I tried to have the code as accessible and and easy to understand as I could so it should be very easy to clone and take a look.
  • Sturla
    Sturla about 5 years
    I created a question on this topic if you liked to help me with this there.
  • Sturla
    Sturla about 5 years
    Just added a bounty to the question with lots more details if you are interested :-)