介绍
您可能注意到了,.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。
总结
虽然实现非常简单,但或许很多人都需要连接请求超时机制,如果有任何问题,我会尽力为您解答。