Unity3d中的Tcp socket通信(开源)

开源地址:https://github.com/hiramtan/HiSocket_unity

HiSocket_unity


如何使用

可以从此链接下载最新的unity package: Github Releases


功能

  • Tcp socket
  • Udp socket
  • 可伸缩字节表
  • 高性能字节块缓冲区
  • 消息注册和回调
  • 二进制字节消息封装
  • Protobuf消息封装
  • AES消息加密

详情

  • Tcp和Udp都是采用主线程异步连接的方式(避免主线程阻塞).
  • 启动发送线程和接收线程处理数据传输(提高性能).
  • 供用户调用发送或接受数据的API在主线程中(方便直接操作unity的组件)
  • 监听连接事件获得当前的连接状态.
  • 监听接收事件获得接收的数据.
  • 存在字节数组队列,方便用来测试和数据重发.
  • 高性能字节缓冲区避免内存空间重复申请,减少GC.
  • 如果使用Tcp协议需要实现IPackage接口处理粘包拆包.
  • Ping接口因为mono底层的bug会在.net2.0平台报错(.net 4.6 没有问题,或者也可以使用unity的接口获得Ping,工程中有示例代码)

细节

  • Tcp Transmission Control Protocol

    • Tcp connection Tcp协议传输字节流,用户需要分割字节流获得正确的数据包,当创建一个tcp协议的socket时,需要传入一个Package对象来封包和解包.

      private IPackage _packer = new Packer();
      void Test()
      {
       _tcp = new TcpConnection(_packer);
      }
      
      public class Packer : IPackage
      {
          public void Unpack(IByteArray reader, Queue<byte[]> receiveQueue)
          {
             //add your unpack logic here
         }
      
         public void Pack(Queue<byte[]> sendQueue, IByteArray writer)
         {
             // add your pack logic here
         }
      }
      
    • 连接

      _tcp.Connect("127.0.0.1", 7777);
      
    • 断开连接 当不再运行时需要主动调用接口断开与服务器的连接(比如响应unity的onapplicationquit执行时)

      void OnApplicationQuit()
      {
          _tcp.DisConnect();
      }
      
    • 连接状态变化 如果想获取当前的连接状态,可以订阅连接状态事件.

      void Test()
      {
          _tcp.StateChangeEvent += OnState;
      }
      void OnState(SocketState state)
      {
          Debug.Log("current state is: " + state);
          if (state == SocketState.Connected)
          {
              Debug.Log("connect success");
              //can send or receive message
          }
          else if (state == SocketState.DisConnected)
          {
              Debug.Log("connect failed");
          }
          else if (state == SocketState.Connecting)
          {
              Debug.Log("connecting");
          }
      }
      
    • 发送消息

      void Test()
      {
          var bytes = BitConverter.GetBytes(100);
          _tcp.Send(bytes);
      }
      
    • 接受消息 You can regist receiveevent and when message come from server, this event will be fire.

          void Test()
          {
              _tcp.ReceiveEvent += OnReceive;
          }
          void OnReceive(byte[] bytes)
          {
              Debug.Log("receive msg: " + BitConverter.ToInt32(bytes, 0));
          }
      
    • 封包和解包 最初创建连接时我们定义了一个packer来分割数据包,当发送消息时我们在数据头部插入消息长度/当接收到消息时我们根据头部的消息长度获得数据包的大小.

      private bool _isGetHead = false;
      private int _bodyLength;
      public void Unpack(IByteArray reader, Queue<byte[]> receiveQueue)
      {
          if (!_isGetHead)
          {
              if (reader.Length >= 2)//2 is example, get msg's head length
              {
                  var bodyLengthBytes = reader.Read(2);
                  _bodyLength = BitConverter.ToUInt16(bodyLengthBytes, 0);
              }
              else
              {
                  if (reader.Length >= _bodyLength)//get body
                  {
                      var bytes = reader.Read(_bodyLength);
                      receiveQueue.Enqueue(bytes);
                      _isGetHead = false;
                  }
              }
          }
      }
      public void Pack(Queue<byte[]> sendQueue, IByteArray writer)
      {
          var bytesWaitToPack = sendQueue.Dequeue();
          UInt16 length = (UInt16)bytesWaitToPack.Length;//get head lenth
          var bytesHead = BitConverter.GetBytes(length);
          writer.Write(bytesHead);//write head
          writer.Write(bytesWaitToPack);//write body
      }
      
  • Udp

    • Udp connection 如果创建upd连接,需要指定发送接收缓冲区大小.

      _udp = new UdpConnection(1024);
      
  • Ping

    public int PingTime;
    private Ping p;
    private float timeOut = 1;
    private float lastTime;
    void Start()
    {
        StartCoroutine(Ping());
    }
    IEnumerator Ping()
    {
        p = new Ping("127.0.0.1");
        lastTime = Time.realtimeSinceStartup;
        while (!p.isDone && Time.realtimeSinceStartup - lastTime < 1)
        {
            yield return null;
        }
        PingTime = p.time;
        p.DestroyPing();
        yield return new WaitForSeconds(1);
        StartCoroutine(Ping());
    }
    
  • 消息注册

  • Protobuf

  • 字节消息

  • 加密


Tcp Example

Tcp 协议提供可靠有序的流字节传输,用户需要自己分割数据,在这个框架中可以继承IPackage接口来实现.

    private ITcp _tcp;
    private IPackage _packer = new Packer();
    // Use this for initialization
    void Start()
    {
        _tcp = new TcpConnection(_packer);
        _tcp.StateChangeEvent += OnState;
        _tcp.ReceiveEvent += OnReceive;
        Connect();
    }
    void Update()
    {
        _tcp.Run();
    }

    void Connect()
    {
        _tcp.Connect("127.0.0.1", 7777);
    }
    // Update is called once per frame

    void OnState(SocketState state)
    {
        Debug.Log("current state is: " + state);
        if (state == SocketState.Connected)
        {
            Debug.Log("connect success");
            Send();
        }
        else if (state == SocketState.DisConnected)
        {
            Debug.Log("connect failed");
        }
        else if (state == SocketState.Connecting)
        {
            Debug.Log("connecting");
        }
    }
    void OnApplicationQuit()
    {
        _tcp.DisConnect();
    }
    void Send()
    {
        for (int i = 0; i < 10; i++)
        {
            var bytes = BitConverter.GetBytes(i);
            Debug.Log("send message: " + i);
            _tcp.Send(bytes);
        }
    }
    void OnReceive(byte[] bytes)
    {
        Debug.Log("receive msg: " + BitConverter.ToInt32(bytes, 0));
    }
    public class Packer : IPackage
    {
        public void Unpack(IByteArray reader, Queue<byte[]> receiveQueue)
        {
            //add your unpack logic here
            if (reader.Length >= 1024)//1024 is example, it's msg's length
            {
                var bytesWaitToUnpack = reader.Read(1024);
                receiveQueue.Enqueue(bytesWaitToUnpack);
            }
        }

        public void Pack(Queue<byte[]> sendQueue, IByteArray writer)
        {
            var bytesWaitToPack = sendQueue.Dequeue();
            // add your pack logic here
            //

            writer.Write(bytesWaitToPack);
        }
    }

Udp Example

User Datagram Protocol

Udp协议提供不可靠的报文消息,用户无法知道当前连接状态,但是消息包时完整的.

    private UdpConnection _udp;
    // Use this for initialization
    void Start()
    {
        _udp = new UdpConnection(1024);
        _udp.ReceiveEvent += OnReceive;
        Connect();
        Send();
    }
    void Connect()
    {
        _udp.Connect("127.0.0.1", 7777);
    }
    // Update is called once per frame
    void Update()
    {
        _udp.Run();
    }
    void Send()
    {
        for (int i = 0; i < 10; i++)
        {
            var bytes = BitConverter.GetBytes(i);
            _udp.Send(bytes);
            Debug.Log("send message: " + i);
        }
    }
    private void OnApplicationQuit()
    {
        _udp.DisConnect();
    }
    void OnReceive(byte[] bytes)
    {
        Debug.Log("receive bytes: " + BitConverter.ToInt32(bytes, 0));
    }

Message Registration Example

    void RegistMsg()
    {
        MsgRegister.Regist("10001", OnMsg_Bytes);
        MsgRegister.Regist("10002", OnMsg_Protobuf);
    }

    void OnMsg_Bytes(IByteArray byteArray)
    {
        var msg = new MsgBytes(byteArray);
        int getInt = msg.Read<int>();
    }

    void OnMsg_Protobuf(IByteArray byteArray)
    {
        var msg = new MsgProtobuf(byteArray);
        GameObject testClass = msg.Read<GameObject>();//your class's type
        var testName = testClass.name;
    }

点击链接加入QQ群【83596104】:https://jq.qq.com/?_wv=1027&k=5l6rZEr

support: [email protected]

猜你喜欢

转载自my.oschina.net/u/2287125/blog/1788611