none
是否可以縮短 Socket TCP 的 Connect Timeout 呢? RRS feed

  • 問題

  • 請教各位前輩:

    小弟以下程式要建立 Socket TCP 通訊連線,但是發現 當 Connect 時,等待時間太長,是否可以縮短 Timeout 呢?

    IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIPAdress), 12345);
    
    Socket sockClientConnect = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
    try
    {
      sockClientConnect.Connect(ipep);
    }
    catch (SocketException eSocketEx)
    {
      MessageBox.Show("Unable to connect to server." + "\n" + eSocketEx.ToString());
    
      return;
    }
    2009年4月10日 上午 03:07

解答

  • 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.


    Cmf.Net (C) 2009
    • 已標示為解答 Carlo Yang 2009年4月10日 上午 04:28
    2009年4月10日 上午 04:05

所有回覆

  • 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.


    Cmf.Net (C) 2009
    • 已標示為解答 Carlo Yang 2009年4月10日 上午 04:28
    2009年4月10日 上午 04:05
  • 天阿。
    太完整了。
    感謝 cmf 前輩提供的資訊。
    謝謝您了。
    2009年4月10日 上午 04:28