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