The TcpClient class in C# is great for opening a TCP connection, I must say that it's one of the nicest TCP libraries i've used. You just have to watch out for the occasional bug and you'll be right.

One limitation is a frustration though: the inability to set a timeout when opening a connection to a remote server. The default timeout is 60 seconds, which is quite a while to have the user strumming their fingers waiting for things to happen.

My solution to this is to spawn a thread which opens the TCP connection, while the original thread waits up to a user-specified timeout for it to connect. If it hasn't connected by then, it kills the thread and gives up. Notice the use of the thread's Join function which allows the original thread to stop waiting if the connection is quicker than the timeout. Without further ado, the TcpClientWithTimeout.cs class:

using System;
using System.Net.Sockets;
using System.Threading;

/// <summary>
/// TcpClientWithTimeout is used to open a TcpClient connection, with a 
/// user definable connection timeout in milliseconds (1000=1second)
/// Use it like this:
/// TcpClient connection = new TcpClientWithTimeout('127.0.0.1',80,1000).Connect();
/// </summary>
public class TcpClientWithTimeout
{
  protected string _hostname;
  protected int _port;
  protected int _timeout_milliseconds;
  protected TcpClient connection;
  protected bool connected;
  protected Exception exception;

  public TcpClientWithTimeout(string hostname,int port,int timeout_milliseconds)
  {
    _hostname = hostname;
    _port = port;
    _timeout_milliseconds = timeout_milliseconds;
  }
  public TcpClient Connect()
  {
    // kick off the thread that tries to connect
    connected = false;
    exception = null;
    Thread thread = new Thread(new ThreadStart(BeginConnect));
    thread.IsBackground = true; // So that a failed connection attempt 
    // wont prevent the process from terminating while it does the long timeout
    thread.Start();

    // wait for either the timeout or the thread to finish
    thread.Join(_timeout_milliseconds);

    if (connected == true) 
    {
      // it succeeded, so return the connection
      thread.Abort();
      return connection;
    }
    if (exception != null) 
    {
      // it crashed, so return the exception to the caller
      thread.Abort();
      throw exception;
    }
    else
    {
      // if it gets here, it timed out, so abort the thread and throw an exception
      thread.Abort();
      string message = string.Format("TcpClient connection to {0}:{1} timed out",
        _hostname, _port);
      throw new TimeoutException(message);
    }
  }
  protected void BeginConnect()
  {
    try
    {
      connection = new TcpClient(_hostname, _port);
      // record that it succeeded, for the main thread to return to the caller
      connected = true;
    }
    catch (Exception ex)
    {
      // record the exception for the main thread to re-throw back to the calling code
      exception = ex; 
    }
  }
}

And here's a little example of how to use this to open a connection, send 10 bytes, and receive 10 bytes:

// connect with a 5 second timeout on the connection
TcpClient connection = new TcpClientWithTimeout("www.google.com", 80, 5000).Connect();
NetworkStream stream = connection.GetStream();

// Send 10 bytes
byte[] to_send = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xa};
stream.Write(to_send, 0, to_send.Length); 

// Receive 10 bytes
byte[] readbuf = new byte[10]; // you must allocate space first
stream.ReadTimeout = 10000; // 10 second timeout on the read
stream.Read(readbuf, 0, 10); // read

// Disconnect nicely
stream.Close(); // workaround for a .net bug: http://support.microsoft.com/kb/821625
connection.Close();

Cheers all, please let me know if this is useful or if you have any constructive criticism.

Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.

Chris Hulbert

(Comp Sci, Hons - UTS)

Software Developer (Freelancer / Contractor) in Australia.

I have worked at places such as Google, Cochlear, Assembly Payments, News Corp, Fox Sports, NineMSN, FetchTV, Coles, Woolworths, Trust Bank, and Westpac, among others. If you're looking for help developing an iOS app, drop me a line!

Get in touch:
[email protected]
github.com/chrishulbert
linkedin



 Subscribe via RSS