TCP框架___Unity

36 篇文章 0 订阅
7 篇文章 0 订阅
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;     //连接服务器
        public const int Exception = -2;   //异常掉线
        public const int Disconnect = -3;  //正常断线   
    }

    enum PACKET_TYPE
    {                              // 包格式(由服务器定下的规矩,客户端严格按照格式发送/接收),PackLen用于标识后续包长,Place为无效占位符,Type为包的类型(PACKET_TYPE),Seq作用未知默认为0,Code为错误码,Body为包正文
        PUSH_TYPE = 0,             // PackLen【2byte】,Place【1byte】,Type【1byte】,Body【最大4096byte】
        REQUSET_TYPE,              // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Body【最大4096byte】
        RESPONSE_TYPE,             // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【2byte】,Code(错误码)【2byte】,Body【最大4096byte】
        PING_TYPE,                 // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS:心跳发送
        PONG_TYPE                  // PackLen【2byte】,Place【1byte】,Type【1byte】,Seq【1byte】PS:心跳回包
    };


    //让byte[] 压入成为lua string 而不是数组 userdata
    //也可以使用LuaByteBufferAttribute来标记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数据结构体,需要注意每次数据写入为有序的
    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; // 用于向服务器发送数据的网络流
        private MemoryStream memStream; // 用于处理从服务器接收数据的内存流,
        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服务器端口
        private string iSocketAddress = string.Empty;          //Socket服务器地址

        private IAsyncResult onReadAsyncResult;

        private static SocketClient _SocketClient;

        private int ConnectionStatus = -1;
        private readonly static byte[] mConnLock = new byte[0];  //链接锁

        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();
        }

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

        // 重连
        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
                };

                // 发起链接
                ConnectionStatus = -1;
                client.BeginConnect(iSocketAddress, iSocketPort, new AsyncCallback(OnConnect), null);
                // 链接超时
                Thread timeout = new Thread(CheckConnectTimeout);
                timeout.IsBackground = true;
                timeout.Start();
            }
            catch (Exception e)
            {
                Debug.Log("Socket ReConnect");
                OnDisconnected(Protocal.Exception, e);
            }
        }

        // 链接超时检测
        private void CheckConnectTimeout()
        {
            lock (mConnLock)
            {
                Monitor.Wait(mConnLock, 10000);
                if (!client.Connected && ConnectionStatus < 0)
                {
                    OnDisconnected(Protocal.Exception, new TimeoutException("Socket Connect Timeout."));
                }
            }
        }

        // 异常统一处理
        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());
            }
        }
        // 异常统一处理
        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)
                {
                    //如果CheckConnectTimeout超时了,这会把client置空了,就别往下走了
                    if (client == null)
                    {
                        return;
                    }
                    // 连接状态
                    ConnectionStatus = client.Connected ? 1 : 0;
                    // 连接失败
                    if (ConnectionStatus == 0)
                    {
                        PostMessage(Protocal.Exception); // PostMessage向Lua端发送消息
                        return;
                    }
                    // 连接成功
                    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())
                {   //读取字节流到缓冲区
                    bytesRead = client.GetStream().EndRead(asr);
                }
                if (bytesRead < 1)
                {   //包尺寸有问题,断线处理
                    OnDisconnected(Protocal.Disconnect, "bytesRead < 1");
                    return;
                }
                //分析数据包内容,抛给逻辑层
                OnReceive(byteBuffer, bytesRead);
                //分析完,再次监听服务器发过来的新消息
                lock (client.GetStream())
                {   //清空数组
                    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);
            }
        }

        // 向服务端发送数据
        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);
            }
        }


        // 发送json格式数据。不同类型的包,数据格式不同,根据需求而定,详情参考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 ==> 不支持该类型 " + 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无效占位符
            buf.WriteByte((byte)type); //1
            buf.WriteShort((ushort)seq); //0
            buf.WriteBytesNoLen(msgByte);

            //Debug.Log("总长度 " + buf.ToBytes().Length + " msg长度 " + 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无效占位符
            buf.WriteByte((byte)type); //3
            buf.WriteByte((byte)seq); //0
            WriteMessage(buf.ToBytes());
        }

        // 数据解析
        private void OnReceive(byte[] bytes, int length)
        {
            // 在流的末端写入读出的字节
            memStream.Seek(0, SeekOrigin.End);
            memStream.Write(bytes, 0, length);
            // 流指针复位至头部
            memStream.Seek(0, SeekOrigin.Begin);

            // 判断当前流长度是否大于包长(包长2字节)
            while (RemainingBytes() >= 2)
            {
                // 前2字节为包头,指明后续的包长
                int messageLen = (int)reader.ReadUInt16();
                long curover = RemainingBytes();
                //Debug.Log("此时读出长度 " + curover + ",数据包应有的长度 " + messageLen);

                // 当接收的数据大于包长时,表明当前包体数据已接收完毕
                if (curover >= messageLen)
                {
                    // 临时内存流,从reader提取需要的字节
                    MemoryStream ms = new MemoryStream();
                    BinaryWriter writer = new BinaryWriter(ms);
                    // 只取当前包长的数据
                    writer.Write(reader.ReadBytes(messageLen));
                    ms.Seek(0, SeekOrigin.Begin);
                    OnReceivedMessage(ms);
                }
                else
                {
                    //当接收数据不完全时,忽略这次执行,并对流指针进行回退操作,等待下次回调
                    memStream.Position = memStream.Position - 2;
                    break;
                }
            }

            int leftovers = (int)RemainingBytes();
            //Debug.Log("解析完毕,剩余字节 " + leftovers);
            //取出剩余字节,并重置内存流,重新写入
            byte[] leftover = reader.ReadBytes(leftovers);
            memStream.SetLength(0);     //Clear
            memStream.Write(leftover, 0, leftover.Length);
        }

        // 查询流长度与当前流指针的差异,用于判断当前流是否满足需要
        private long RemainingBytes()
        {
            return memStream.Length - memStream.Position;
        }

        //解析数据包内容(除包长2字节以外的内容)
        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 类型,需要再次解析4个字节才是正文
                if (type == (int)PACKET_TYPE.RESPONSE_TYPE)
                {
                    seq = r.ReadUInt16(); // Seq【2byte】
                    code = r.ReadUInt16(); // Code【2byte】
                }
                // pong 类型,心跳回包
                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);

                    // 服务端发送包正文为json,直接转换成string,由需求而定
                    Debug.Log("Received Msg " + Encoding.UTF8.GetString(buffer.buffer));
                    PostMessage(0, buffer);
                }
            }
        }


        // 向客户端回调消息,待定,cmd为命令字,buffer为则是实际数据
        protected void PostMessage(int cmd, object buffer = null)
        {
            // 此接口为原项目工程所有,用于向lua端发送消息,把proto2反序列化成lua表
            // 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;
            }
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值