当前位置:网站首页>TCP framework___ Unity

TCP framework___ Unity

2022-07-07 15:53:00 Le_ Sam

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using UnityEngine;
using System.Text;

namespace Core
{
    public class Protocal
    {
        public const int Connect = -1;     // Connect to server 
        public const int Exception = -2;   // Abnormal dropped line 
        public const int Disconnect = -3;  // Normal disconnection    
    }

    enum PACKET_TYPE
    {                              //  Packet format ( Rules set by the server , The client sends strictly according to the format / receive ),PackLen Used to identify the subsequent package length ,Place Is an invalid placeholder ,Type Is the type of package (PACKET_TYPE),Seq The function is unknown. The default is 0,Code Is the error code ,Body For the body of the package 
        PUSH_TYPE = 0,             // PackLen【2byte】,Place【1byte】,Type【1byte】,Body【 Maximum 4096byte】
        REQUSET_TYPE,              // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Body【 Maximum 4096byte】
        RESPONSE_TYPE,             // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Code( Error code )【2byte】,Body【 Maximum 4096byte】
        PING_TYPE,                 // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS: Heartbeat sending 
        PONG_TYPE                  // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS: Heartbeat back 
    };


    // Give Way byte[]  Press into lua string  Not an array  userdata
    // You can also use LuaByteBufferAttribute To mark byte[]
    public struct LuaByteBuffer
    {
        public LuaByteBuffer(IntPtr source, int len)
            : this()
        {
            buffer = new byte[len];
            Length = len;
            Marshal.Copy(source, buffer, 0, len);
        }

        public LuaByteBuffer(byte[] buf)
            : this()
        {
            buffer = buf;
            Length = buf.Length;
        }

        public LuaByteBuffer(byte[] buf, int len)
            : this()
        {
            buffer = buf;
            Length = len;
        }

        public LuaByteBuffer(System.IO.MemoryStream stream)
            : this()
        {
            buffer = stream.GetBuffer();
            Length = (int)stream.Length;
        }

        public static implicit operator LuaByteBuffer(System.IO.MemoryStream stream)
        {
            return new LuaByteBuffer(stream);
        }

        public byte[] buffer;

        public int Length
        {
            get;
            private set;
        }
    }

    // Byte Data structures , It should be noted that each time data is written in order 
    public class ByteBuffer
    {
        MemoryStream stream = null;
        BinaryWriter writer = null;
        BinaryReader reader = null;

        public ByteBuffer()
        {
            stream = new MemoryStream();
            writer = new BinaryWriter(stream);
        }

        public ByteBuffer(byte[] data)
        {
            if (data != null)
            {
                stream = new MemoryStream(data);
                reader = new BinaryReader(stream);
            }
            else
            {
                stream = new MemoryStream();
                writer = new BinaryWriter(stream);
            }
        }

        public void Close()
        {
            if (writer != null)
            {
                writer.Close();
            }
            if (reader != null)
            {
                reader.Close();
            }

            stream.Close();
            writer = null;
            reader = null;
            stream = null;
        }

        public void WriteByte(byte v)
        {
            writer.Write(v);
        }

        public void WriteInt(int v)
        {
            writer.Write((int)v);
        }

        public void WriteShort(ushort v)
        {
            writer.Write((ushort)v);
        }

        public void WriteLong(long v)
        {
            writer.Write((long)v);
        }

        public void WriteFloat(float v)
        {
            byte[] bytes = BitConverter.GetBytes(v);
            Array.Reverse(bytes);
            writer.Write(BitConverter.ToSingle(bytes, 0));
        }

        public void WriteDouble(double v)
        {
            byte[] bytes = BitConverter.GetBytes(v);
            Array.Reverse(bytes);
            writer.Write(BitConverter.ToDouble(bytes, 0));
        }

        public void WriteString(string v)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(v);
            writer.Write((ushort)bytes.Length);
            writer.Write(bytes);
        }

        public void WriteBytes(byte[] v)
        {
            writer.Write((int)v.Length);
            writer.Write(v);
        }

        public void WriteBytesNoLen(byte[] v)
        {
            writer.Write(v);
        }

        public void WriteBuffer(LuaByteBuffer strBuffer)
        {
            WriteBytes(strBuffer.buffer);
        }

        public byte ReadByte()
        {
            return reader.ReadByte();
        }

        public int ReadInt()
        {
            return (int)reader.ReadInt32();
        }

        public ushort ReadShort()
        {
            return (ushort)reader.ReadInt16();
        }

        public long ReadLong()
        {
            return (long)reader.ReadInt64();
        }

        public float ReadFloat()
        {
            byte[] bytes = BitConverter.GetBytes(reader.ReadSingle());
            Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }

        public double ReadDouble()
        {
            byte[] bytes = BitConverter.GetBytes(reader.ReadDouble());
            Array.Reverse(bytes);
            return BitConverter.ToDouble(bytes, 0);
        }

        public string ReadString()
        {
            ushort len = ReadShort();
            byte[] buffer = new byte[len];
            buffer = reader.ReadBytes(len);
            return Encoding.UTF8.GetString(buffer);
        }

        public byte[] ReadBytes()
        {
            int len = ReadInt();
            return reader.ReadBytes(len);
        }

        public LuaByteBuffer ReadBuffer()
        {
            byte[] bytes = ReadBytes();
            return new LuaByteBuffer(bytes);
        }

        public byte[] ToBytes()
        {
            writer.Flush();
            return stream.ToArray();
        }

        public void Flush()
        {
            writer.Flush();
        }
    }

    public class SocketClient
    {
        private TcpClient client = null;
        private NetworkStream outStream = null; //  Network flow used to send data to the server 
        private MemoryStream memStream; //  Used to process the memory stream that receives data from the server ,
        private BinaryReader reader;

        private const int HEAD_BASE_LEN = 2;
        private const int MAX_READ = 8192;

        private byte[] byteBuffer = new byte[MAX_READ];

        private int iSocketPort = 0;                           //Socket Server port 
        private string iSocketAddress = string.Empty;          //Socket Server address 

        private IAsyncResult onReadAsyncResult;

        private static SocketClient _SocketClient;

        private int ConnectionStatus = -1;
        private readonly static byte[] mConnLock = new byte[0];  // Link lock 

        public static SocketClient GetSocketClient()
        {
            return _SocketClient;
        }
        public static void SetSocketClient(SocketClient socketClient)
        {
            _SocketClient = socketClient;
        }

        public SocketClient()
        {
            memStream = new MemoryStream();
            reader = new BinaryReader(memStream);
            SocketClient.SetSocketClient(this);
        }

        ~SocketClient()
        {
            Close();
            reader.Close();
            memStream.Close();
        }

        //  Connect 
        public void Connect(string addr, int port)
        {
            iSocketAddress = addr;
            iSocketPort = port;
            ReConnect();
        }

        //  Reconnection 
        private void ReConnect()
        {
            try
            {
                IPAddress ipAddress;
                if (!ParseIpFromHost(iSocketAddress, out ipAddress))
                {
                    OnDisconnected(Protocal.Exception, string.Format("Parse ip from {0} failed", iSocketAddress));
                    return;
                }

                client = new TcpClient(ipAddress.AddressFamily)
                {
                    SendTimeout = 10000,
                    ReceiveTimeout = 10000,
                    NoDelay = true
                };

                //  Initiate a link 
                ConnectionStatus = -1;
                client.BeginConnect(iSocketAddress, iSocketPort, new AsyncCallback(OnConnect), null);
                //  Connection timeout 
                Thread timeout = new Thread(CheckConnectTimeout);
                timeout.IsBackground = true;
                timeout.Start();
            }
            catch (Exception e)
            {
                Debug.Log("Socket ReConnect");
                OnDisconnected(Protocal.Exception, e);
            }
        }

        //  Link timeout detection 
        private void CheckConnectTimeout()
        {
            lock (mConnLock)
            {
                Monitor.Wait(mConnLock, 10000);
                if (!client.Connected && ConnectionStatus < 0)
                {
                    OnDisconnected(Protocal.Exception, new TimeoutException("Socket Connect Timeout."));
                }
            }
        }

        //  Exception handling 
        private void OnDisconnected(int protocal, Exception e)
        {
            Close();
            PostMessage(protocal);
            if (e is SocketException)
            {
                Debug.Log(e.ToString() + " SocketErrorCode: " + ((SocketException)(e)).ErrorCode);
            }
            else
            {
                Debug.Log(e.ToString());
            }
        }
        //  Exception handling 
        private void OnDisconnected(int protocal, string msg)
        {
            Close();
            PostMessage(protocal);
            Debug.Log("SocketErrorCode: " + msg);
        }

        private static bool ParseIpFromHost(string host, out IPAddress address)
        {
            address = null;
            try
            {
                if (!IPAddress.TryParse(host, out address))
                {
                    var entries = Dns.GetHostEntry(host);
                    var addressLst = entries.AddressList.ToList();
                    address = addressLst.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetworkV6) ?? addressLst.First();
                }

                return true;
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return false;
            }
        }

        private void OnConnect(IAsyncResult asr)
        {
            memStream.SetLength(0);
            try
            {
                lock (mConnLock)
                {
                    // If CheckConnectTimeout It's overtime , This will turn client It's empty , Don't go down 
                    if (client == null)
                    {
                        return;
                    }
                    //  Connection status 
                    ConnectionStatus = client.Connected ? 1 : 0;
                    //  The connection fails 
                    if (ConnectionStatus == 0)
                    {
                        PostMessage(Protocal.Exception); // PostMessage towards Lua End send message 
                        return;
                    }
                    //  Successful connection 
                    outStream = client.GetStream();
                    onReadAsyncResult = client.GetStream().BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), null);
                    PostMessage(Protocal.Connect);
                    Monitor.PulseAll(mConnLock);
                }
            }
            catch (Exception e)
            {
                OnDisconnected(Protocal.Exception, e);
            }
        }

        private void OnRead(IAsyncResult asr)
        {
            if (client == null || !client.Connected)
            {
                return;
            }

            int bytesRead = 0;
            try
            {
                lock (client.GetStream())
                {   // Read byte stream to buffer 
                    bytesRead = client.GetStream().EndRead(asr);
                }
                if (bytesRead < 1)
                {   // There is a problem with the size of the bag , Disconnection treatment 
                    OnDisconnected(Protocal.Disconnect, "bytesRead < 1");
                    return;
                }
                // Analyze packet content , Throw it to the logic layer 
                OnReceive(byteBuffer, bytesRead);
                // Analysis finished , Listen again for new messages from the server 
                lock (client.GetStream())
                {   // Empty array 
                    Array.Clear(byteBuffer, 0, byteBuffer.Length);   
                    onReadAsyncResult = client.GetStream().BeginRead(byteBuffer, 0, MAX_READ, new AsyncCallback(OnRead), null);
                }
            }
            catch (Exception e)
            {
                OnDisconnected(Protocal.Exception, e);
            }
        }

        //  Send data to the server 
        private void WriteMessage(byte[] message)
        {
            if (client == null || !client.Connected)
            {
                return;
            }
            try
            {
                outStream.BeginWrite(message, 0, message.Length, new AsyncCallback(OnWrite), null);
            }
            catch (Exception e)
            {
                OnDisconnected(Protocal.Exception, e);
            }
        }

        private void OnWrite(IAsyncResult asr)
        {
            if (client == null || !client.Connected)
            {
                return;
            }

            try
            {
                outStream.EndWrite(asr);
            }
            catch (Exception e)
            {
                OnDisconnected(Protocal.Exception, e);
            }
        }


        //  send out json Format data . Different types of packages , The data format is different , According to demand , Details refer to PACKET_TYPE
        public void Send(int type, int seq, string msg)
        {
            //Debug.Log("Send type: " + type.ToString() + " seq: " + seq.ToString() + " msg: " + msg);
            switch (type)
            {
                case (int)PACKET_TYPE.REQUSET_TYPE:
                    SendRequset(type, seq, msg);
                    break;
                case (int)PACKET_TYPE.PING_TYPE:
                    SendPing(type, seq);
                    break;
                default:
                    Debug.Log("SocketClientEx ==>  The type is not supported  " + msg);
                    return;
            }
        }

        private void SendRequset(int type, int seq, string msg)
        {
            ByteBuffer buf = new ByteBuffer();
            byte[] msgByte = Encoding.UTF8.GetBytes(msg);
            buf.WriteShort((ushort)(msgByte.Length + 4)); // Place+Type+Seq = 4
            buf.WriteByte(0); // Place Invalid placeholder 
            buf.WriteByte((byte)type); //1
            buf.WriteShort((ushort)seq); //0
            buf.WriteBytesNoLen(msgByte);

            //Debug.Log(" Total length  " + buf.ToBytes().Length + " msg length  " + msgByte.Length);
            WriteMessage(buf.ToBytes());
        }

        private void SendPing(int type, int seq)
        {
            ByteBuffer buf = new ByteBuffer();
            buf.WriteShort(3); // Place+Type+Seq = 3
            buf.WriteByte(0); // Place Invalid placeholder 
            buf.WriteByte((byte)type); //3
            buf.WriteByte((byte)seq); //0
            WriteMessage(buf.ToBytes());
        }

        //  Data analysis 
        private void OnReceive(byte[] bytes, int length)
        {
            //  Write the read bytes at the end of the stream 
            memStream.Seek(0, SeekOrigin.End);
            memStream.Write(bytes, 0, length);
            //  The flow pointer is reset to the head 
            memStream.Seek(0, SeekOrigin.Begin);

            //  Judge whether the current stream length is greater than the packet length ( Bag length 2 byte )
            while (RemainingBytes() >= 2)
            {
                //  front 2 Byte is the header , Indicate the subsequent package length 
                int messageLen = (int)reader.ReadUInt16();
                long curover = RemainingBytes();
                //Debug.Log(" Now read the length  " + curover + ", The length of the packet  " + messageLen);

                //  When the received data is larger than the packet length , Indicates that the current inclusion data has been received 
                if (curover >= messageLen)
                {
                    //  Temporary memory stream , from reader Extract the required bytes 
                    MemoryStream ms = new MemoryStream();
                    BinaryWriter writer = new BinaryWriter(ms);
                    //  Only get the data of the current packet length 
                    writer.Write(reader.ReadBytes(messageLen));
                    ms.Seek(0, SeekOrigin.Begin);
                    OnReceivedMessage(ms);
                }
                else
                {
                    // When the received data is incomplete , Ignore this execution , And back out the pointer , Wait for the next callback 
                    memStream.Position = memStream.Position - 2;
                    break;
                }
            }

            int leftovers = (int)RemainingBytes();
            //Debug.Log(" End of analysis , The remaining bytes  " + leftovers);
            // Take out the remaining bytes , And reset the memory stream , To write 
            byte[] leftover = reader.ReadBytes(leftovers);
            memStream.SetLength(0);     //Clear
            memStream.Write(leftover, 0, leftover.Length);
        }

        //  The difference between the query stream length and the current stream pointer , It is used to judge whether the current flow meets the needs 
        private long RemainingBytes()
        {
            return memStream.Length - memStream.Position;
        }

        // Parse the packet contents ( Except package length 2 Contents other than bytes )
        private void OnReceivedMessage(MemoryStream ms)
        {
            if (ms.Length >= HEAD_BASE_LEN)
            {
                BinaryReader r = new BinaryReader(ms);
                int place = r.ReadByte(); // Place【1byte】
                int type = r.ReadByte(); // Type【1byte】
                int seq = 0;
                int code = 0;
                // response  type , Need to parse again 4 Bytes are the body 
                if (type == (int)PACKET_TYPE.RESPONSE_TYPE)
                {
                    seq = r.ReadUInt16(); // Seq【2byte】
                    code = r.ReadUInt16(); // Code【2byte】
                }
                // pong  type , Heartbeat back 
                else if (type == (int)PACKET_TYPE.PONG_TYPE)
                {
                    seq = r.ReadByte(); // Seq【1byte】
                }

                if (ms.Length - ms.Position > 0)
                {
                    byte[] body = r.ReadBytes((int)(ms.Length - ms.Position));
                    LuaByteBuffer buffer = new LuaByteBuffer(body);

                    //  The body of the package sent by the server is json, Convert directly to string, It depends on the demand 
                    Debug.Log("Received Msg " + Encoding.UTF8.GetString(buffer.buffer));
                    PostMessage(0, buffer);
                }
            }
        }


        //  Callback message to client , undetermined ,cmd Is the command word ,buffer Is the actual data 
        protected void PostMessage(int cmd, object buffer = null)
        {
            //  This interface is owned by the original project , Used to direct to lua End send message , hold proto2 Antisequential formation lua surface 
            // MessageCenter.Post(MessageNames.Socket, this, cmd, buffer);


        }


        public void Close()
        {
            if (onReadAsyncResult != null)
            {
                onReadAsyncResult.AsyncWaitHandle.Close();
                onReadAsyncResult = null;
            }
            if (client != null)
            {
                if (client.Connected)
                {
                    if (client.GetStream() != null)
                    {
                        client.GetStream().Dispose();
                    }
                    if (client.Client.Connected)
                    {
                        client.Client.Shutdown(SocketShutdown.Both);
                    }
                    client.Close();
                }
                client = null;
            }
        }
    }
}

 

原网站

版权声明
本文为[Le_ Sam]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202130609270762.html