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






4.89/5 (19投票s)
使用套接字编程连接到目标机器并设置超时
引言
你会注意到,这两个类System.Net.Sockets.TcpClient
和System.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
超时时,它将返回超时异常。否则,套接字成功连接或遇到任何网络错误。
这里使用tcpclient
的BeginConnect
,因为此方法不会阻塞。调用BeginConnect
后,我们使用waitone
等待。如果BeginConnect
在超时内未完成操作,则waitone
将发出信号并返回TimeoutException
。如果BeginConnect
在超时内完成操作,则它将使用我们在BeginInvoke
中作为委托传递的CallBackMethod
(该委托引用在操作完成后调用的CallBackMethod
)来发出ManualResetEvent
的TimeoutObject.Set()
信号。
示例代码
这里附带了一个项目,展示了“套接字连接超时”功能。
结论
虽然这是一个非常简单的任务,但我发布了它,因为我认为很多人像我一样需要这个套接字编程功能。如果您有任何问题,我很乐意回答。
历史
- 初始发布 – 2008年5月12日