Net的System.Net.Sockets.TcpClient和System.Net.Sockets.Socket都没有直接为Connect/BeginConnect提供超时控制机制。因此,当服务器未处于监听状态,或者发生网络故障时,客户端连接请求会被迫等待很长一段时间,直到抛出异常。默认的等待时间长达20~30s。.Net Socket库的SocketOptionName命名空间里提供了发送和接收的超时时间,但首先那是基于同步的方法,第二也没有超时连接时间的设置,所以嘛,自己写咯

首先做些准备工作

1 private TcpClient client;
2 private NetworkStream netStream;

把这俩声明为类变量,方便统一控制 

private EventWaitHandle eventHandle;

这个变量是为控制回调方法无法解除绑定的问题而做的处理…具体看后面,谁要知道咋解挂回调方法告一声啊=.=

然后就是 Connect 方法

View Code
        /// <summary>
        /// 连接
        /// </summary>
        /// <param name=”ip”>要连接的IP</param>
        public bool Connect(string ip)
        {
            eventHandle = new EventWaitHandle(false, EventResetMode.ManualReset); 
 
            try
            {
                isProcessing = true;
                eventHandle.Reset();

                // 开始异步连接
                client = new TcpClient(AddressFamily.InterNetwork);
                client.BeginConnect(IPAddress.Parse(ip), 8001, RequestCallback, client);

                // 判断超时,若超时,进行相应处理
                if (!eventHandle.WaitOne(1500, false))
                {
                    client.Client.Close(); // 彻底关闭TCP连接,下同
                    isProcessing = false;
                    Console.WriteLine(ip + ” 连接超时”); // test,所有非异常输出语句均为测试用
                    return false;
                }
                else
                {
                    // 判断client.Connected是为避免本机无连接直接返回
                    if (client.Connected)
                    {
                        Console.WriteLine(ip + ” 连接成功”);
                        netStream = client.GetStream();
                        return true;
                    }

                    return false;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(“Connect() “ + ex.Message);
                return false;
            }
        }

结合回调方法一起看

View Code
        /// <summary>
        /// 连接回调方法
        /// </summary>
        private void RequestCallback(IAsyncResult ar)
        {
            if (isProcessing)
            {
                eventHandle.Set();

                try
                {
                    client = (TcpClient)ar.AsyncState;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(“RequestCallback() “ + ex.Message);
                }
            }
        } 

可以看出 eventHandle.ReSet() eventHandle.Set() 是这个机制的实现基础, EventWaitHandle 表示一个线程同步事件.EventWaitHandle 类允许线程通过发信号互相通信. 通常,一个或多个线程在 EventWaitHandle 上阻止,直到一个未阻止的线程调用 Set 方法,以释放一个或多个被阻止的线程.而手动设置MSDN是这么解释的:手动重置事件类似于入口.当事件不处于终止状态时,在该事件上等待的线程将阻止。.当事件处于终止状态时,所有等待的线程都被释放,而事件一直保持终止状态(即后面的等待不阻止),直到它的 Reset 方法被调用.如果一个线程必须完成一项活动后,其他线程才能继续,则手动重置事件很有用. 详细:http://msdn.microsoft.com/zh-cn/library/system.threading.eventwaithandle.aspx

在这个示例里,主线程调用 eventHandle.ReSet() 让其他需要等待的线程阻塞,然后 WaitOne(Int32, Boolean) 阻塞主线程等待信号,在回调函数里调用 eventHandle.Set() 使等待的线程运行,以达到判超时的目的



这里有两个需要特别注意的地方:

                // 判断超时,若超时,进行相应处理
                if (!eventHandle.WaitOne(1500, false))
                {
                    client.Client.Close(); // 彻底关闭TCP连接,下同
                    isProcessing = false;
                    Console.WriteLine(ip + ” 连接超时”); // test,所有非异常输出语句均为测试用
                    return false;
                }
                else
                {
                    // 判断client.Connected是为避免本机无连接直接返回
                    if (client.Connected)
                    {
                        Console.WriteLine(ip + ” 连接成功”);
                        netStream = client.GetStream();
                        return true;
                    }

                    return false;
                }

client.Client.Close() 这里要调用 client 对象的 Client 属性,是因为 本身 client.Close() 并没有关闭基础TCP连接,所以要设置基础Socket.

还有就是回调函数的返回问题,其实这里挺奇怪的,调项目时有次不在办公室,在自己那恰好没网,结果一运行回调函数还是立马返回了,所以要通过判断 client.Connected 属性来确保判超时的正确.

posted on 2012-06-05 08:53  iou90  阅读(798)  评论(0编辑  收藏  举报