近日,在进行PPC下的网络通讯程序开发时,当服务器的网络中断或连接异常时,客户端在进行TCP连接请求时“Socket.Connect()”速度明显过慢,通常20-30S才能有异常返回。.Net的System.Net.Sockets.TcpClient和System.Net.Sockets.Socket都没有直接为Connect/BeginConnect提供超时控制机制。因此,当服务器未处于监听状态,或者发生网络故障时,客户端连接请求会被迫等待很长一段时间,直到抛出异常。为解决这一问题,我查阅了部分资料同时进行了测试,现将解决方法整理如下:
1、部分代码段:
private ManualResetEvent connectDone = new ManualResetEvent(false);
private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket) ar.AsyncState;
client.EndConnect(ar);
}
catch (Exception e)
{
OnErrorEvent(new ErrorEventArgs(e));
}
finally
{
connectDone.Set();
}
}
/// <summary>
/// 开始连接的方法
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="selfIP"></param>
/// <param name="selfPort"></param>
/// <returns></returns>
public bool ClientConnect(IPAddress ip, string port, string selfIP, string selfPort)
{
try
{
IPEndPoint remoteEP = new IPEndPoint(ip, Int32.Parse(port));////网络端点表示为 IP 地址和端口号
//TCP连接
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//开始连接
connectDone.Reset();
clientSocket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), clientSocket);
connectDone.WaitOne(5000, false);//等待5秒
//连接成功
if (clientSocket.Connected)
{
//do something
}
return true;
}
catch (Exception)
{
return false;
}
}
这里,ManualResetEvent的WaitOne(TimeSpan, Boolean)起到了主要的作用。它将阻止当前线程,直到ManualResetEvent对象被Set或者超过timeout时间。上面的代码中,调用BeginConnect后通过WaitOne方法阻止当前线程,如果在timeoutMSec时间内连接成功,将在CallBackMethod回调中调用connectDone.Set,解除被阻塞的连接线程并返回。
经过测试上面只是解决了连接速度慢的问题(网络异常时),设备可以在连接中断后自动重新连接服务器。但是,我发现当连接不正常时,clientSocket.BeginConnect得到的Socket的Connected属性仍为true,所以要想确保连接的可靠性只能采用信息交互的方式(即客户端向服务端发送要求反馈的信息,并能成功接收到反馈信息时才是正常的连接)。