GComm_TcpClient.cs 8.17 KB
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace GeneralGommunication
{
    public class GComm_TcpClient : IGeneralComm
    {
        public Logger logger;// = NLog.LogManager.GetCurrentClassLogger();
        /// <summary>
        /// IP 地址 and 端口号
        /// </summary>
        public string Addr { get; set; }

        IPEndPoint RemoteEp => Misc.StringConverter.ToIPEndPoint(Addr);

        /// <summary>
        /// 是否异常
        /// </summary>
        public bool IsError => !string.IsNullOrEmpty(ErrMsg);

        /// <summary>
        /// 异常信息
        /// </summary>
        public string ErrMsg { get; private set; }

        /// <summary>
        /// 运行中
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 连接成功
        /// </summary>
        public bool IsConnected { get; private set; }

        /// <summary>
        /// 接收task调用
        /// </summary>
        public event IGeneralCommDataReceivedHandler DataReceived;



        public event PropertyChangedEventHandler PropertyChanged;

        Socket sock;
        CancellationTokenSource cts_readTask;
        /// <summary>
        /// sock 的线程锁
        /// </summary>
        object sock_lock = new object();
        Task task = null;

        public GComm_TcpClient()
        {

        }
        /// <summary>
        /// 开始
        /// </summary>
        public void Start()
        {
            if (IsRunning)
                return;
            IsRunning = true;

            if (task == null || task.IsCompleted)
            {
                //线程已经完成, 创建个新的
                cts_readTask = new CancellationTokenSource();
                task = Task.Factory.StartNew(OnTask, cts_readTask.Token);
            }
            else 
            {
                //线程还没结束,等待结束后,再创建新的
                task.Wait();

                cts_readTask = new CancellationTokenSource();
                task = Task.Factory.StartNew(OnTask, cts_readTask.Token);
            }
        }

        /// <summary>
        /// 接收
        /// </summary>
        public void Stop()
        {
            if (!IsRunning)
                return;
            IsRunning = false;
            IsConnected = false;
            cts_readTask.Cancel();
            closeSock();
        }
        /// <summary>
        /// 关闭sock,打断接收,发送中的等待,非OnTask调用
        /// </summary>
        void closeSock() 
        {
            lock (sock_lock)
            {
                if (sock != null && sock.Connected)
                {
                    sock.Close();
                }
            }
        }
        /// <summary>
        /// 释放sock, 只能在OnTask中调用
        /// </summary>
        void disposeSock() 
        {
            lock (sock_lock)//lock住,防止外部线程在 sock==null 时,调用sock.Close()
            {
                if (sock != null)
                {
                    sock.Close();
                    sock = null;
                }
            }
        }
        void OnTask()
        {
            while (!cts_readTask.IsCancellationRequested)
            {
                ConnectTask();

                if (IsConnected)
                {
                    //进入接收task
                    ReceiveTask();

                    //退出了,肯定连接断开了
                    disposeSock();
                }

                //休息一会儿,再重连
                try
                {
                    Task.Delay(2000, cts_readTask.Token).Wait();
                }
                catch (Exception e)
                {
                    //被打断了
                    break;
                }
            }
            disposeSock();
            IsConnected = false;
        }

        /// <summary>
        /// 连接任务
        /// </summary>
        void ConnectTask()
        {
            while (!cts_readTask.IsCancellationRequested)
            {
                try
                {
                    //sock 的 创建,销毁,只能在 这个线程操作
                    sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    sock.Blocking = true;

                    //1秒内,需要收到信息
                    sock.ReceiveTimeout = 1000;
                    //1秒内,必须发送完
                    sock.SendTimeout = 1000;

                    //sock.Connect(RemoteEp);
                    //Connect(sock, RemoteEp, 2000);
                    var result = sock.BeginConnect(RemoteEp, null, null);
                    var sucess = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));
                    if (sucess)
                    {
                        sock.EndConnect(result);
                        ErrMsg = null;
                        IsConnected = true;
                        return;
                    }
                    else
                    {
                        disposeSock();
                        ErrMsg = "连接超时";
                    }
                }
                catch (Exception e)
                {
                    disposeSock();
                    //连接出错
                    //等待重试
                    ErrMsg = e.Message;
                }
               
                try
                {
                    Task.Delay(2000, cts_readTask.Token).Wait();
                }
                catch (Exception e)
                {
                    //被打断
                    return;
                }
            }
        }

        /// <summary>
        /// 接收任务
        /// </summary>
        void ReceiveTask()
        {
            byte[] buf = new byte[sock.ReceiveBufferSize];
            while (!cts_readTask.IsCancellationRequested)
            {
                int len;
                try
                {
                    len = sock.Receive(buf);
                    if (len == 0)
                    {
                        continue;//没收到数据,继续等
                    }
                }
                catch (SocketException e)
                {
                    if (e.SocketErrorCode == SocketError.TimedOut)
                        continue;//超时而已
                    else if ((e.SocketErrorCode == SocketError.ConnectionAborted)
                        || (e.SocketErrorCode == SocketError.ConnectionRefused)
                        || (e.SocketErrorCode == SocketError.ConnectionReset))
                    {
                        IsConnected = false;
                        ErrMsg = e.Message;
                        return;
                    }
                    //Console.WriteLine($"ReceiveTask() {e}");
                    //肯定断开连接了
                    ErrMsg = e.Message;
                    IsConnected = false;
                    return;
                }
                logger?.Debug($"tcp R len={len}");
                DataReceived?.Invoke(this, buf.Take(len).ToArray());
            }

            IsConnected = false;
        }


        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buf"></param>
        public void Write(IEnumerable<byte> buf)
        {
            if (!IsConnected)
                return;

            int slen;
            try
            {
                slen = sock.Send(buf.ToArray());
                logger?.Debug($"tcp W len={slen}");
            }
            catch (Exception e)
            {
                //连接断开了
                ErrMsg = e.Message;
                IsConnected = false;
                return;
            }

            if (slen <= 0)
            {
                //连接断开了
                ErrMsg = "发送数量小于0";
                IsConnected = false;
                return;
            }
        }

        /// <summary>
        /// 清空输入缓存
        /// </summary>
        public void DiscardInBuffer()
        {
            //TODO
        }



    }
}