《Unity3D網絡遊戲實踐》(第2版)要點摘錄 – 「通信協議與消息隊列基礎」

書名:《Unity3D網絡遊戲實踐》(第2版)

作者:羅培羽

所讀版本:機械工業出版社

通信協議與消息隊列

  • 通信協議:把協議名、客戶端標識、具體參數組成一個固定格式的字符串,在服務端和客戶端之間傳輸,再根據協議格式解析出數據使用/轉發

    • 如:消息名|客戶端IP與端口, 參數1, 參數2, 參數n

  • 消息隊列在Unity裡只有主線程可以操作UI組件,多線程消息處理容易導致各種奇怪的混亂,因此可以使用消息隊列讓主線程去處理Socket異步接收到的信息

    • C#異步通信由線程池實現,不同的BeginReceive不一定在同一線程中執行

    • 創建一個由主線程讀取的消息列表,每當收到消息便在列表末端添加數據

      • 數據從頂端讀取,讀取處理後將其從頂端移除

  • public static class NetManager {
       static Socket socket;
       //接收緩沖區
       static byte[] readBuff = new byte[1024];
       //委托類型
       public delegate void MsgListener(string str);
       //監聽列表(各個消息名對應的處理方法)
       private static Dictionary<string, MsgListener> listeners = new Dictionary<string, MsgListener>();
       //消息隊列
       static List<string> msgList = new List<string>();
       
       //記錄消息對應的處理方法
       public static void AddListener(string msgName, MsgListener listener)
      {
           listeners[msgName] = listener;
      }

       public static void Connect(string ip, int port)
      {
           socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
           socket.Connect(ip, port); //同步連接
           socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket); //異步接收信息
      }

       static void ReceiveCallback(IAsyncResult ar)
      {
           try
          {
               Socket socket = (Socket)ar.AsyncState;
               int count = socket.EndReceive(ar);
               string recvStr = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);

               //消息隊列更新
               msgList.Add(recvStr);

               socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);
          }
           catch(SocketException ex)
          {
               Debug.Log("Socket Receive Failed: " + ex.ToString());
          }
      }

       public static void Send(string sendStr)
      {
           if (socket == null) { return; }
           if (!socket.Connected) { return; }

           //同步發送
           byte[] sendBytes = System.Text.Encoding.UTF8.GetBytes(sendStr);
           socket.Send(sendBytes);
      }

       //每幀更新,持續處理消息隊列中的消息
       public static void Update()
      {
           if (msgList.Count <= 0) { return; }
           string msgStr = msgList[0];
           msgList.RemoveAt(0);

           string[] split = msgStr.Split('|');
           string msgName = split[0];
           string msgArgs = split[1];

           if (listeners.ContainsKey(msgName))
          {
               listeners[msgName](msgArgs);
          }
      }
    }