1. 程式人生 > >C# Socket.Connect連線請求超時機制

C# Socket.Connect連線請求超時機制

作者:RazanPaul
譯者:Todd Wei

原文:http://www.codeproject.com/KB/IP/TimeOutSocket.aspx
轉自:http://hi.baidu.com/wf_studio/blog/item/d0a339f4c70c9ae57609d7c9.html

介紹
您可能注意到了,.Net的System.Net.Sockets.TcpClient和System.Net.Sockets.Socket都沒有直接為Connect/BeginConnect提供超
時控制機制。因此,當伺服器未處於監聽狀態,或者發生網路故障時,客戶端連線請求會被迫等待很長一段時間,直到丟擲異常。默
認的等待時間長達20~30s。.Net Socket庫的SocketOptionName.SendTimeout提供了控制傳送資料的超時時間,但並非本文討論的連
接請求的超時時間。

背景
這個問題最初源於我的某個專案,在解決以後,我曾將關鍵程式碼發表在自己的部落格上。我注意到不少人對此表示感謝,所以我想這是
一個常見的問題,或許很多人都需要解決它。

實現

下面是實現的關鍵程式碼:

class TimeOutSocket
{
    private static bool IsConnectionSuccessful = false;
    private static Exception socketexception;
    private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);

    public static TcpClient Connect(IPEndPoint remoteEndPoint, int timeoutMSec)
    {
        TimeoutObject.Reset();
        socketexception = null; 

        string serverip = Convert.ToString(remoteEndPoint.Address);
        int serverport = remoteEndPoint.Port;           
        TcpClient tcpclient = new TcpClient();
        
        tcpclient.BeginConnect(serverip, serverport, 
            new AsyncCallback(CallBackMethod), tcpclient);

        if (TimeoutObject.WaitOne(timeoutMSec, false))
        {
            if (IsConnectionSuccessful)
            {
                return tcpclient;
            }
            else
            {
                throw socketexception;
            }
        }
        else
        {
            tcpclient.Close();
            throw new TimeoutException("TimeOut Exception");
        }
    }
    private static void CallBackMethod(IAsyncResult asyncresult)
    {
        try
        {
            IsConnectionSuccessful = false;
            TcpClient tcpclient = asyncresult.AsyncState as TcpClient;
             
            if (tcpclient.Client != null)
            {
                tcpclient.EndConnect(asyncresult);
                IsConnectionSuccessful = true;
            }
        }
        catch (Exception ex)
        {
            IsConnectionSuccessful = false;
            socketexception = ex;
        }
        finally
        {
            TimeoutObject.Set();
        }
    }
}

這裡,ManualResetEvent的WaitOne(TimeSpan, Boolean)起到了主要的作用。它將阻止當前執行緒,直到ManualResetEvent物件被Set
或者超過timeout時間。上面的程式碼中,呼叫BeginConnect後通過WaitOne方法阻止當前執行緒,如果在timeoutMSec時間內連線成功,
將在CallBackMethod回撥中呼叫TimeoutObject.Set,解除被阻塞的連線執行緒並返回;否則,連線執行緒會在等待超時後,主動關閉連
接並丟擲TimeoutException。