c#给tcpclient connect()设置超时

  1. socket 底层Connect()方法,如果存在端口错误,或是监控端口server 未开启则,connect()会在10~30返回结果。
    这个时间太长了,非常影响用户体验。
  2. StackOverflow的相关链接:
    1
    代码测试
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Sockets;
using System.Net;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Method3();
            Console.ReadKey();
        }
        static bool  Method3()
        {
            var client = new TcpClient();
            if (!client.ConnectAsync("192.168.1.131", 50000).Wait(1000))
            {
                //连接失败
                Console.WriteLine("连接失败");
                return  false;
            }
            Console.WriteLine("连接成功");
            return true;
          
        }
    }
}

  1. 使用委托beginInvoke()的轮询模式。轮询模式
    在调用beginInvoke时将新建一个线程异步执行,使用轮询模式,判断方式是否已执行完成,并判断其是否超时。
    缺点: 当超时时,无法abort在运行的线程。
    代码测试
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    delegate void Mydel();
    class Program
    {
        static void Main(string[] args)
        {
            ///使用轮询模式,监控异步方法是否超时
            Mydel del = new Mydel(Method);
            Stopwatch watch = new Stopwatch();
            watch.Start();

            IAsyncResult iar = del.BeginInvoke(null, null);//开始异步调用
            Console.WriteLine("After BeginInvoke");
            while (!iar.IsCompleted)
            {
                Console.WriteLine("Not Done");

                //判断是否超时,若超时,则重启读写器
                long time = watch.ElapsedMilliseconds;
                if (time > 1500)
                {
                    Console.WriteLine("异步方法超时");
                    //超时 
                    break;
                }

                System.Threading.Thread.Sleep(100);
                Console.WriteLine("下一轮查询");
            }
            Console.ReadKey();
        }
        /// <summary>
        /// 需要异步执行的方法
        /// </summary>
        static void Method()
        {
            Console.WriteLine("进入方法内部");
            ///耗时操作
            System.Threading.Thread.Sleep(4000);
            Console.WriteLine("方法结束");
        }
    }

}

运行结果
从运行结果看到, 并不能取消异步正在运行的线程。被异步调用的方法,在判断已超时后,仍在运行。
有没有可以取消已在运行的线程呢?使用CancellationTokenSource看一下取消线程运行的机制

  1. CancellationTokenSource:
    msdn
    csdn
    代码测试:
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Method();
            Console.ReadKey();
        }

        static void Method()
        {
            var CancellationTokenSource = new CancellationTokenSource(3000);
            Task.Factory.StartNew(() =>
            {
                while (!CancellationTokenSource.IsCancellationRequested)
                {
                    Console.WriteLine(DateTime.Now);
                    Thread.Sleep(1000);
                }
            }, CancellationTokenSource.Token);

            //Console.WriteLine("Press any to cancel");
            //Console.ReadLine();
            //CancellationTokenSource.Cancel();

            Console.WriteLine("done");
            Console.ReadLine();
        }
    }
}

但是实际的业务需求中,并不能用CancellationTokenSource来取消正在运行的线程。
如果用来封装第三方的某个方法以实现可以自定义超时时间,使用CancellationTokenSource是不行。

  1. 使用线程: 2
    代码测试
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                 TcpClient connection = new TcpClientWithTimeout("192.168.1.131", 50000, 1000).Connect();
               if(connection.Client != null)
                {
                    Console.WriteLine("连接成功");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("连接失败");
                Console.WriteLine(e.Message);
            }
            Console.ReadKey();
        }

       
    }

    public class TcpClientWithTimeout
    {
        protected string _hostname;
        protected int _port;
        protected int _timeout_milliseconds;
        protected TcpClient connection;
        protected bool connected;
        protected Exception exception;

        public TcpClientWithTimeout(string hostname, int port, int timeout_milliseconds)
        {
            _hostname = hostname;
            _port = port;
            _timeout_milliseconds = timeout_milliseconds;
        }
        public TcpClient Connect()
        {
            // kick off the thread that tries to connect
            connected = false;
            exception = null;
            Thread thread = new Thread(new ThreadStart(BeginConnect));
            thread.IsBackground = true; // So that a failed connection attempt 
                                        // wont prevent the process from terminating while it does the long timeout
            thread.Start();

            // wait for either the timeout or the thread to finish
            thread.Join(_timeout_milliseconds);

            if (connected == true)
            {
                // it succeeded, so return the connection
                thread.Abort();
                return connection;
            }
            if (exception != null)
            {
                // it crashed, so return the exception to the caller
                thread.Abort();
                throw exception;
            }
            else
            {
                // if it gets here, it timed out, so abort the thread and throw an exception
                thread.Abort();
                string message = string.Format("TcpClient connection to {0}:{1} timed out",
                  _hostname, _port);
                throw new TimeoutException(message);
            }
        }
        protected void BeginConnect()
        {
            try
            {
                connection = new TcpClient(_hostname, _port);
                // record that it succeeded, for the main thread to return to the caller
                connected = true;
            }
            catch (Exception ex)
            {
                // record the exception for the main thread to re-throw back to the calling code
                exception = ex;
            }
        }
    }
}

这个方式虽然不是太简介,但是实现了在方法执行超时时,取消在已开启的线程。可以有效管理线程资源。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值