65.9K
CodeProject 正在变化。 阅读更多。
Home

在 C# 中实现带超时的套接字连接

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (19投票s)

2008年12月5日

CPOL

2分钟阅读

viewsIcon

213488

downloadIcon

3658

使用套接字编程连接到目标机器并设置超时

引言

你会注意到,这两个类System.Net.Sockets.TcpClientSystem.Net.Sockets.Socket都没有设置套接字连接超时的选项。我的意思是无法设置的超时。.NET 套接字在建立同步/异步套接字连接时调用Connect/BeginConnect方法时不提供连接超时。相反,如果尝试连接的服务器没有监听或存在任何网络错误,连接将被迫等待很长时间才能抛出异常。默认超时时间为 20-30 秒。套接字库中有一个名为SocketOptionName.SendTimeout的选项,用于发送数据超时,而不是初始连接。

背景

在为我的一个项目解决问题后,我在我的博客上发布了这段代码片段,用于使用套接字编程在设置超时的情况下连接目标机器。许多人都为此表示感谢。所以我觉得如果我把这段代码发布到Code Project上,很多需要的人都能看到它。出于这个原因,我把它发布到这里。我知道这是一个非常简单的任务,不应该成为Code Project上的文章。

Using the Code

这里这个功能被实现为一个类。该类的代码如下

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,它有一个重载WaitOne(TimeSpan, Boolean)。根据 MSDN 的说法,WaitOne(TimeSpan, Boolean)阻塞当前线程,直到当前实例收到信号,使用TimeSpan来测量时间间隔,并指定是否在等待之前退出同步域。

所以在主线程中,我们调用TimeoutObject.WaitOne(timeoutMSec, false)来阻塞主线程,直到超时或收到使用TimeoutObject.Set()发出的信号。当waitone超时时,它将返回超时异常。否则,套接字成功连接或遇到任何网络错误。

这里使用tcpclientBeginConnect,因为此方法不会阻塞。调用BeginConnect后,我们使用waitone等待。如果BeginConnect在超时内未完成操作,则waitone将发出信号并返回TimeoutException。如果BeginConnect在超时内完成操作,则它将使用我们在BeginInvoke中作为委托传递的CallBackMethod(该委托引用在操作完成后调用的CallBackMethod)来发出ManualResetEventTimeoutObject.Set()信号。

示例代码

这里附带了一个项目,展示了“套接字连接超时”功能。

结论

虽然这是一个非常简单的任务,但我发布了它,因为我认为很多人像我一样需要这个套接字编程功能。如果您有任何问题,我很乐意回答。

历史

  • 初始发布 – 2008年5月12日
© . All rights reserved.