← 筆記

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