您可能注意到了,.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
socket
exception;
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。