Unity3d中的Tcp socket通訊(開源)

開源地址:https://github.com/hiramtan/HiSocket_unitygit

HiSocket_unity


如何使用

能夠今後連接下載最新的unity package: Github Releasesgithub


功能

  • 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
         }
      }
    • 鏈接app

      _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");
          }
      }
    • 發送消息socket

      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.tcp

      void Test()
          {
              _tcp.ReceiveEvent += OnReceive;
          }
          void OnReceive(byte[] bytes)
          {
              Debug.Log("receive msg: " + BitConverter.ToInt32(bytes, 0));
          }
    • 封包和解包 最初建立鏈接時咱們定義了一個packer來分割數據包,當發送消息時咱們在數據頭部插入消息長度/當接收到消息時咱們根據頭部的消息長度得到數據包的大小.wordpress

      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: hiramtan@live.com

相關文章
相關標籤/搜索