URTcpClient.cs 12.8 KB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Net.Sockets;
using log4net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace URSoldering.Common
{
    public class URTcpClient
    {
        public static readonly ILog LOGGER = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        public delegate void ByteHandleMessage(byte[] data);
        public int DefaultDataLength = 1024;
        public int ReviceSleepMS = 100;
        private Socket m_clientSocket = null;
        private byte[] m_receiveBuffer = new byte[1078];
        private string LogName = "";

        private ByteHandleMessage byteOnReceived;
        public int TimeOutTime = 0;

        /// <summary>
        ///  当前连接状态
        /// </summary> 
        public bool IsConnected()
        {
            if (m_clientSocket == null)
            {
                return false;
            }
            if (m_clientSocket.Connected)
            {
                return true;
            }

            try
            {
                #region 过程
                // This is how you can determine whether a socket is still connected.
                bool connectState = true;
                bool blockingState = m_clientSocket.Blocking;
                try
                {
                    byte[] tmp = new byte[1];

                    m_clientSocket.Blocking = false;
                    m_clientSocket.Send(tmp, 0, 0);
                    //Console.WriteLine("Connected!");
                    connectState = true; //若Send错误会跳去执行catch体,而不会执行其try体里其之后的代码
                }
                catch (SocketException e)
                {
                    // 10035 == WSAEWOULDBLOCK
                    if (e.NativeErrorCode.Equals(10035))
                    {
                        connectState = true;
                    }
                    else
                    {
                        connectState = false;
                    }
                }
                finally
                {
                    if (m_clientSocket != null && m_clientSocket.Connected)
                    {
                        m_clientSocket.Blocking = blockingState;
                    }
                }

                return connectState;
                #endregion
            }
            catch (Exception ex)
            {
                LogUtil.debug(LOGGER, "出错啦" + ex.ToString());
                return false;
            }
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        public bool connect(string serverIP, int serverPort, ByteHandleMessage byteHandle)
        {
            LogName = "【" + serverIP + " ," + serverPort + "】";
            m_receiveBuffer = new byte[DefaultDataLength];
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            if (TimeOutTime <= 0)
            {
                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
                m_clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                try
                {
                    if (!m_clientSocket.Connected)
                    {
                        m_clientSocket.Connect(remoteEndPoint);
                    }
                    if (m_clientSocket.Connected)
                    {
                        m_clientSocket.BeginReceive(m_receiveBuffer, 0, m_receiveBuffer.Length, 0, new AsyncCallback(ReceiveCallBack), null);
                        byteOnReceived = byteHandle;
                        LogUtil.URLInfo("Connect to " + LogName + " success");
                        return true;
                    }
                    else
                    {
                        LogUtil.URLInfo("Connect to " + LogName + " fail");
                    }
                }
                catch (Exception ex)
                {
                    LogUtil.URLError("Connect to " + LogName + " error :" + ex.ToString());
                    //m_clientSocket = null;
                }
            }
            else
            {
                m_clientSocket.ReceiveTimeout = TimeOutTime;
                m_clientSocket.SendTimeout = TimeOutTime;
                IAsyncResult connResult = m_clientSocket.BeginConnect(serverIP, serverPort, null, null);
                connResult.AsyncWaitHandle.WaitOne(this.TimeOutTime, true);  //等待2秒
                if (!connResult.IsCompleted || (!m_clientSocket.Connected))
                {
                    LogUtil.URLInfo("Connect to " + LogName + " fail");
                    m_clientSocket.Close();
                    //处理连接不成功的动作
                    return false;
                }
                else
                {
                    //处理连接成功的动作
                    m_clientSocket.BeginReceive(m_receiveBuffer, 0, m_receiveBuffer.Length, 0, new AsyncCallback(ReceiveCallBack), null);
                    byteOnReceived = byteHandle;
                    if (byteHandle != null)
                    {
                        byteOnReceived = byteHandle;
                    }
                    LogUtil.URLInfo("Connect to " + LogName + " success");
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void close()
        {
            try
            {
                if (m_clientSocket != null && m_clientSocket.Connected)
                {
                    m_clientSocket.Shutdown(SocketShutdown.Both);
                    m_clientSocket.Close();
                    LogUtil.URLInfo(LogName + "Socket closed!");
                }
                else
                {
                    LogUtil.URLInfo(LogName + "No socket is running!");
                }
            }
            catch (Exception ex)
            {
                LogUtil.URLError(LogName + "close error :" + ex.ToString());
            }
        }

        /// <summary>
        /// 发送信息
        /// </summary>
        public void send(string strSendData)
        {
            byte[] sendBuffer = new byte[DefaultDataLength];
            sendBuffer = Encoding.UTF8.GetBytes(strSendData);

            if (m_clientSocket != null && m_clientSocket.Connected)
            {
                m_clientSocket.Send(sendBuffer);
                LogUtil.debug(LOGGER, "Send >> " + strSendData);
            }
        }
        /// <summary>
        /// 发送信息
        /// </summary>
        public void sendLine(string strSendData)
        {
            LogUtil.info(LOGGER, LogName + "发送数据:" + strSendData);
            LogUtil.URLInfo(LogName + "发送数据:" + strSendData);
            strSendData = strSendData + "\r\n";
            byte[] sendBuffer = new byte[DefaultDataLength];
            sendBuffer = Encoding.UTF8.GetBytes(strSendData);
            if (m_clientSocket != null && m_clientSocket.Connected)
            {
                m_clientSocket.Send(sendBuffer);
                LogUtil.debug(LOGGER, "Send >> " + strSendData);
            }
        }
        int headSize = 4;//包头长度 固定4
        byte[] surplusBuffer = null;//不完整的数据包,即用户自定义缓冲区
        /// <summary>
        /// 接收客户端发来的数据
        /// </summary>
        /// <param name="connId">每个客户的会话ID</param>
        /// <param name="bytes">缓冲区数据</param>
        /// <returns></returns>
        private int OnReceive(byte[] bytes)
        {
            try
            {
                //bytes 为系统缓冲区数据
                //bytesRead为系统缓冲区长度
                int bytesRead = bytes.Length;
                if (bytesRead > 0)
                {
                    if (surplusBuffer == null)//判断是不是第一次接收,为空说是第一次
                    {
                        surplusBuffer = bytes;//把系统缓冲区数据放在自定义缓冲区里面
                    }
                    else
                    {
                        surplusBuffer = surplusBuffer.Concat(bytes).ToArray();//拼接上一次剩余的包
                    }
                    //已经完成读取每个数据包长度
                    int haveRead = 0;
                    //这里totalLen的长度有可能大于缓冲区大小的(因为 这里的surplusBuffer 是系统缓冲区+不完整的数据包)
                    int totalLen = surplusBuffer.Length;
                    while (haveRead <= totalLen)
                    {
                        //如果在N此拆解后剩余的数据包连一个包头的长度都不够
                        //说明是上次读取N个完整数据包后,剩下的最后一个非完整的数据包
                        if (totalLen - haveRead < headSize)
                        {
                            byte[] byteSub = new byte[totalLen - haveRead];
                            //把剩下不够一个完整的数据包存起来
                            Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
                            surplusBuffer = byteSub;
                            totalLen = 0;
                            break;
                        }
                        //如果够了一个完整包,则读取包头的数据
                        byte[] headByte = new byte[headSize];
                        Buffer.BlockCopy(surplusBuffer, haveRead, headByte, 0, headSize);//从缓冲区里读取包头的字节
                        headByte = surplusBuffer.Skip(haveRead).Take(headSize).ToArray().Reverse<byte>().ToArray();
                        int bodySize = BitConverter.ToInt32(headByte, 0);//从包头里面分析出包体的长度
                        if (bodySize <= 0)
                        {
                            LogUtil.error("解析错误:长度:" + bodySize);
                        }
                        //这里的 haveRead=等于N个数据包的长度 从0开始;0,1,2,3....N
                        //如果自定义缓冲区拆解N个包后的长度 大于 总长度,说最后一段数据不够一个完整的包了,拆出来保存
                        if (haveRead + bodySize > totalLen)
                        {
                            byte[] byteSub = new byte[totalLen - haveRead];
                            Buffer.BlockCopy(surplusBuffer, haveRead, byteSub, 0, totalLen - haveRead);
                            surplusBuffer = byteSub;
                            break;
                        }
                        else
                        {
                            //挨个分解每个包,解析成实际文字
                            byte[] endArray = surplusBuffer.Skip(haveRead).Take(bodySize).ToArray();// 
                            Task.Factory.StartNew(delegate ()
                            {
                                byteOnReceived?.Invoke(endArray);
                            });
                            //依次累加当前的数据包的长度
                            haveRead = haveRead + bodySize;
                            if (bodySize == bytesRead)//如果当前接收的数据包长度正好等于缓冲区长度,则待拼接的不规则数据长度归0
                            {
                                surplusBuffer = null;//设置空 回到原始状态
                                totalLen = 0;//清0
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogUtil.error(LOGGER, " 分包出错:" + ex.ToString());
            }
            return 1;
        }

        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                if (m_clientSocket != null && m_clientSocket.Connected)
                {
                    int REnd = m_clientSocket.EndReceive(ar);
                    //OnReceive(m_receiveBuffer);
                    Task.Factory.StartNew(delegate ()
                    {
                        byteOnReceived(m_receiveBuffer);
                    });
                    Thread.Sleep(ReviceSleepMS);
                    //LOGGER.Debug("m_clientSocket:" + m_clientSocket + "\n m_receiveBuffer" + m_receiveBuffer);
                    //Task.Factory.StartNew(delegate ()
                    //{
                    m_clientSocket.BeginReceive(m_receiveBuffer, 0, m_receiveBuffer.Length, 0, new AsyncCallback(ReceiveCallBack), null);
                    //});
                }
            }
            catch (Exception ex)
            {
                LogUtil.URLError(LogName + " socket received error:" + ex.ToString());
            }
        }
    }
}