C#使用Protocol Buffer(ProtoBuf)进行Unity中的Socket通信
From:?http://www.jb51.net/article/82795.htm
首先來說一下本文中例子所要實現的功能:
- 基于ProtoBuf序列化對象
- 使用Socket實現時時通信
- 數據包的編碼和解碼
下面來看具體的步驟:
一、Unity中使用ProtoBuf
導入DLL到Unity中,
創建網絡傳輸的模型類:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | using System; using ProtoBuf; //添加特性,表示可以被ProtoBuf工具序列化 [ProtoContract] public class NetModel { ?//添加特性,表示該字段可以被序列化,1可以理解為下標 ?[ProtoMember(1)] ?public int ID; ?[ProtoMember(2)] ?public string Commit; ?[ProtoMember(3)] ?public string Message; } using System; using ProtoBuf; ?? //添加特性,表示可以被ProtoBuf工具序列化 [ProtoContract] public class NetModel { ?//添加特性,表示該字段可以被序列化,1可以理解為下標 ?[ProtoMember(1)] ?public int ID; ?[ProtoMember(2)] ?public string Commit; ?[ProtoMember(3)] ?public string Message; } |
在Unity中添加測試腳本,介紹ProtoBuf工具的使用。
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | using System; using System.IO; public class Test : MonoBehaviour { ?void Start () { ??//創建對象 ??NetModel item = new NetModel(){ID = 1, Commit = "LanOu", Message = "Unity"}; ??//序列化對象 ??byte[] temp = Serialize(item); ??//ProtoBuf的優勢一:小 ??Debug.Log(temp.Length); ??//反序列化為對象 ??NetModel result = DeSerialize(temp); ??Debug.Log(result.Message); ?} ?// 將消息序列化為二進制的方法 ?// < param name="model">要序列化的對象< /param> ?private byte[] Serialize(NetModel model) ?{ ??try { ???//涉及格式轉換,需要用到流,將二進制序列化到流中 ???using (MemoryStream ms = new MemoryStream()) { ????//使用ProtoBuf工具的序列化方法 ????ProtoBuf.Serializer.Serialize<NetModel> (ms, model); ????//定義二級制數組,保存序列化后的結果 ????byte[] result = new byte[ms.Length]; ????//將流的位置設為0,起始點 ????ms.Position = 0; ????//將流中的內容讀取到二進制數組中 ????ms.Read (result, 0, result.Length); ????return result; ???} ??} catch (Exception ex) { ???Debug.Log ("序列化失敗: " + ex.ToString()); ???return null; ??} ?} ?// 將收到的消息反序列化成對象 ?// < returns>The serialize.< /returns> ?// < param name="msg">收到的消息.</param> ?private NetModel DeSerialize(byte[] msg) ?{ ??try { ???using (MemoryStream ms = new MemoryStream()) { ????//將消息寫入流中 ????ms.Write (msg, 0, msg.Length); ????//將流的位置歸0 ????ms.Position = 0; ????//使用工具反序列化對象 ????NetModel result = ProtoBuf.Serializer.Deserialize<NetModel> (ms); ????return result; ???} ??} catch (Exception ex) {? ????Debug.Log("反序列化失敗: " + ex.ToString()); ????return null; ??} ?} } using System; using System.IO; ?? public class Test : MonoBehaviour { ?? ?void Start () { ??//創建對象 ??NetModel item = new NetModel(){ID = 1, Commit = "LanOu", Message = "Unity"}; ??//序列化對象 ??byte[] temp = Serialize(item); ??//ProtoBuf的優勢一:小 ??Debug.Log(temp.Length); ??//反序列化為對象 ??NetModel result = DeSerialize(temp); ??Debug.Log(result.Message); ?? ?} ?? ?// 將消息序列化為二進制的方法 ?// < param name="model">要序列化的對象< /param> ?private byte[] Serialize(NetModel model) ?{ ??try { ???//涉及格式轉換,需要用到流,將二進制序列化到流中 ???using (MemoryStream ms = new MemoryStream()) { ????//使用ProtoBuf工具的序列化方法 ????ProtoBuf.Serializer.Serialize<NetModel> (ms, model); ????//定義二級制數組,保存序列化后的結果 ????byte[] result = new byte[ms.Length]; ????//將流的位置設為0,起始點 ????ms.Position = 0; ????//將流中的內容讀取到二進制數組中 ????ms.Read (result, 0, result.Length); ????return result; ???} ??} catch (Exception ex) { ???Debug.Log ("序列化失敗: " + ex.ToString()); ???return null; ??} ?} ?? ?// 將收到的消息反序列化成對象 ?// < returns>The serialize.< /returns> ?// < param name="msg">收到的消息.</param> ?private NetModel DeSerialize(byte[] msg) ?{ ??try { ???using (MemoryStream ms = new MemoryStream()) { ????//將消息寫入流中 ????ms.Write (msg, 0, msg.Length); ????//將流的位置歸0 ????ms.Position = 0; ????//使用工具反序列化對象 ????NetModel result = ProtoBuf.Serializer.Deserialize<NetModel> (ms); ????return result; ???} ??} catch (Exception ex) {? ????Debug.Log("反序列化失敗: " + ex.ToString()); ????return null; ??} ?} } |
二、Unity中使用Socket實現時時通信
通信應該實現的功能:
- 服務器可以時時監聽多個客戶端
- 服務器可以時時監聽某一個客戶端消息
- 服務器可以時時給某一個客戶端發消息
- 首先我們需要定義一個客戶端對象
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | using System; using System.Net.Sockets; // 表示一個客戶端 public class NetUserToken { ?//連接客戶端的Socket ?public Socket socket; ?//用于存放接收數據 ?public byte[] buffer; ?public NetUserToken() ?{ ??buffer = new byte[1024]; ?} ?// 接受消息 ?// < param name="data">Data.< /param> ?public void Receive(byte[] data) ?{ ??UnityEngine.Debug.Log("接收到消息!"); ?} ?// 發送消息 ?//< param name="data">Data.< /param> ?public void Send(byte[] data) ?{? ?} } using System; using System.Net.Sockets; ?? // 表示一個客戶端 public class NetUserToken { ?//連接客戶端的Socket ?public Socket socket; ?//用于存放接收數據 ?public byte[] buffer; ?? ?public NetUserToken() ?{ ??buffer = new byte[1024]; ?} ?? ?// 接受消息 ?// < param name="data">Data.< /param> ?public void Receive(byte[] data) ?{ ??UnityEngine.Debug.Log("接收到消息!"); ?} ?? ?// 發送消息 ?//< param name="data">Data.< /param> ?public void Send(byte[] data) ?{? ?? ?} } |
然后實現我們的服務器代碼
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | using System.Collections; using System.Collections.Generic; using System.Net; using System; using System.Net.Sockets; public class NetServer{ ?//單例腳本 ?public static readonly NetServer Instance = new NetServer(); ?//定義tcp服務器 ?private Socket server; ?private int maxClient = 10; ?//定義端口 ?private int port = 35353; ?//用戶池 ?private Stack<NetUserToken> pools; ?private NetServer() ?{ ??//初始化socket ??server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ??server.Bind(new IPEndPoint(IPAddress.Any, port)); ?} ?//開啟服務器 ?public void Start() ?{ ??server.Listen(maxClient); ??UnityEngine.Debug.Log("Server OK!"); ??//實例化客戶端的用戶池 ??pools = new Stack<NetUserToken>(maxClient); ??for(int i = 0; i < maxClient; i++) ??{ ???NetUserToken usertoken = new NetUserToken(); ???pools.Push(usertoken); ??} ??//可以異步接受客戶端, BeginAccept函數的第一個參數是回調函數,當有客戶端連接的時候自動調用 ??server.BeginAccept (AsyncAccept, null); ?} ?//回調函數, 有客戶端連接的時候會自動調用此方法 ?private void AsyncAccept(IAsyncResult result) ?{ ??try { ???//結束監聽,同時獲取到客戶端 ???Socket client = server.EndAccept(result); ???UnityEngine.Debug.Log("有客戶端連接"); ???//來了一個客戶端 ???NetUserToken userToken = pools.Pop(); ???userToken.socket = client; ???//客戶端連接之后,可以接受客戶端消息 ???BeginReceive(userToken); ???//尾遞歸,再次監聽是否還有其他客戶端連入 ???server.BeginAccept(AsyncAccept, null); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?//異步監聽消息 ?private void BeginReceive(NetUserToken userToken) ?{ ??try { ???//異步方法 ???userToken.socket.BeginReceive(userToken.buffer, 0, userToken.buffer.Length, SocketFlags.None, ???????????EndReceive, userToken); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?//監聽到消息之后調用的函數 ?private void EndReceive(IAsyncResult result) ?{ ??try { ???//取出客戶端 ???NetUserToken userToken = result.AsyncState as NetUserToken; ???//獲取消息的長度 ???int len = userToken.socket.EndReceive(result); ???if(len > 0) ???{ ????byte[] data = new byte[len]; ????Buffer.BlockCopy(userToken.buffer, 0, data, 0, len); ????//用戶接受消息 ????userToken.Receive(data); ????//尾遞歸,再次監聽客戶端消息 ????BeginReceive(userToken); ???} ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} } using System.Collections; using System.Collections.Generic; using System.Net; using System; using System.Net.Sockets; ?? public class NetServer{ ?//單例腳本 ?public static readonly NetServer Instance = new NetServer(); ?//定義tcp服務器 ?private Socket server; ?private int maxClient = 10; ?//定義端口 ?private int port = 35353; ?//用戶池 ?private Stack<NetUserToken> pools; ?private NetServer() ?{ ??//初始化socket ??server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ??server.Bind(new IPEndPoint(IPAddress.Any, port)); ?? ?} ?? ?//開啟服務器 ?public void Start() ?{ ??server.Listen(maxClient); ??UnityEngine.Debug.Log("Server OK!"); ??//實例化客戶端的用戶池 ??pools = new Stack<NetUserToken>(maxClient); ??for(int i = 0; i < maxClient; i++) ??{ ???NetUserToken usertoken = new NetUserToken(); ???pools.Push(usertoken); ??} ??//可以異步接受客戶端, BeginAccept函數的第一個參數是回調函數,當有客戶端連接的時候自動調用 ??server.BeginAccept (AsyncAccept, null); ?} ?? ?//回調函數, 有客戶端連接的時候會自動調用此方法 ?private void AsyncAccept(IAsyncResult result) ?{ ??try { ???//結束監聽,同時獲取到客戶端 ???Socket client = server.EndAccept(result); ???UnityEngine.Debug.Log("有客戶端連接"); ???//來了一個客戶端 ???NetUserToken userToken = pools.Pop(); ???userToken.socket = client; ???//客戶端連接之后,可以接受客戶端消息 ???BeginReceive(userToken); ?? ???//尾遞歸,再次監聽是否還有其他客戶端連入 ???server.BeginAccept(AsyncAccept, null); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?? ?//異步監聽消息 ?private void BeginReceive(NetUserToken userToken) ?{ ??try { ???//異步方法 ???userToken.socket.BeginReceive(userToken.buffer, 0, userToken.buffer.Length, SocketFlags.None, ???????????EndReceive, userToken); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?? ?//監聽到消息之后調用的函數 ?private void EndReceive(IAsyncResult result) ?{ ??try { ???//取出客戶端 ???NetUserToken userToken = result.AsyncState as NetUserToken; ???//獲取消息的長度 ???int len = userToken.socket.EndReceive(result); ???if(len > 0) ???{ ????byte[] data = new byte[len]; ????Buffer.BlockCopy(userToken.buffer, 0, data, 0, len); ????//用戶接受消息 ????userToken.Receive(data); ????//尾遞歸,再次監聽客戶端消息 ????BeginReceive(userToken); ???} ?? ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} } |
在Unity中開啟服務器,并使用C#控制臺模擬客戶端連接、發送消息操作。測試OK了,Unity中可以時時監聽到消息。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | using UnityEngine; using System.Collections; public class CreateServer : MonoBehaviour { ?void StartServer () { ??NetServer.Instance.Start(); ?} } //C#控制臺工程 using System; using System.Net; using System.Net.Sockets; using System.IO; using System.Text; namespace Temp { ?class MainClass ?{ ??public static void Main (string[] args) ??{ ???TcpClient tc = new TcpClient(); ???IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 35353); ???tc.Connect(ip); ???if(tc.Connected) ???{ ????while(true) ????{ ?????string msg = Console.ReadLine(); ?????byte[] result = Encoding.UTF8.GetBytes(msg); ?????tc.GetStream().Write(result, 0, result.Length); ????} ???} ???Console.ReadLine(); ??} ?} } using UnityEngine; using System.Collections; ?? public class CreateServer : MonoBehaviour { ?? ?void StartServer () { ??NetServer.Instance.Start(); ?} ?? } ?? //C#控制臺工程 ?? using System; using System.Net; using System.Net.Sockets; using System.IO; using System.Text; ?? namespace Temp { ?class MainClass ?{ ??public static void Main (string[] args) ??{ ???TcpClient tc = new TcpClient(); ???IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 35353); ???tc.Connect(ip); ?? ???if(tc.Connected) ???{ ????while(true) ????{ ?? ?????string msg = Console.ReadLine(); ?????byte[] result = Encoding.UTF8.GetBytes(msg); ?????tc.GetStream().Write(result, 0, result.Length); ????} ???} ???Console.ReadLine(); ??} ?} } |
三、數據包的編碼和解碼
首先,舉個例子,這個月信用卡被媳婦刷爆了,面對房貸車貸的壓力,我只能選擇分期付款。。。
那么OK了,現在我想問一下,當服務器向客戶端發送的數據過大時怎么辦呢?
當服務器需要向客戶端發送一條很長的數據,也會“分期付款!”,服務器會把一條很長的數據分成若干條小數據,多次發送給客戶端。
可是,這樣就又有另外一個問題,客戶端接受到多條數據之后如何解析?
這里其實就是客戶端的解碼。server發數據一般采用“長度+內容”的格式,Client接收到數據之后,先提取出長度來,然后根據長度判斷內容是否發送完畢。
再次重申,用戶在發送序列化好的消息的前,需要先編碼后再發送消息;用戶在接受消息后,需要解碼之后再解析數據(反序列化)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | using UnityEngine; using System.Collections.Generic; using System.IO; // 編碼和解碼 public class NetEncode { ?// 將數據編碼 長度+內容 ?/// < param name="data">內容< /param> ?public static byte[] Encode(byte[] data) ?{ ??//整形占四個字節,所以聲明一個+4的數組 ??byte[] result = new byte[data.Length + 4]; ??//使用流將編碼寫二進制 ??MemoryStream ms = new MemoryStream(); ??BinaryWriter br = new BinaryWriter(ms); ??br.Write(data.Length); ??br.Write(data); ??//將流中的內容復制到數組中 ??System.Buffer.BlockCopy(ms.ToArray(), 0, result, 0, (int)ms.Length); ??br.Close(); ??ms.Close(); ??return result; ?} ?// 將數據解碼 ?// < param name="cache">消息隊列< /param> ?public static byte[] Decode(ref List<byte> cache) ?{ ??//首先要獲取長度,整形4個字節,如果字節數不足4個字節 ??if(cache.Count < 4) ??{ ???return null; ??} ??//讀取數據 ??MemoryStream ms = new MemoryStream(cache.ToArray()); ??BinaryReader br = new BinaryReader(ms); ??int len = br.ReadInt32(); ??//根據長度,判斷內容是否傳遞完畢 ??if(len > ms.Length - ms.Position) ??{ ???return null; ??} ??//獲取數據 ??byte[] result = br.ReadBytes(len); ??//清空消息池 ??cache.Clear(); ??//講剩余沒處理的消息存入消息池 ??cache.AddRange(br.ReadBytes((int)ms.Length - (int)ms.Position)); ??return result; ?} } using UnityEngine; using System.Collections.Generic; using System.IO; ?? // 編碼和解碼 public class NetEncode { ?? ?// 將數據編碼 長度+內容 ?/// < param name="data">內容< /param> ?public static byte[] Encode(byte[] data) ?{ ??//整形占四個字節,所以聲明一個+4的數組 ??byte[] result = new byte[data.Length + 4]; ??//使用流將編碼寫二進制 ??MemoryStream ms = new MemoryStream(); ??BinaryWriter br = new BinaryWriter(ms); ??br.Write(data.Length); ??br.Write(data); ??//將流中的內容復制到數組中 ??System.Buffer.BlockCopy(ms.ToArray(), 0, result, 0, (int)ms.Length); ??br.Close(); ??ms.Close(); ??return result; ?} ?? ?// 將數據解碼 ?// < param name="cache">消息隊列< /param> ?public static byte[] Decode(ref List<byte> cache) ?{ ??//首先要獲取長度,整形4個字節,如果字節數不足4個字節 ??if(cache.Count < 4) ??{ ???return null; ??} ??//讀取數據 ??MemoryStream ms = new MemoryStream(cache.ToArray()); ??BinaryReader br = new BinaryReader(ms); ??int len = br.ReadInt32(); ??//根據長度,判斷內容是否傳遞完畢 ??if(len > ms.Length - ms.Position) ??{ ???return null; ??} ??//獲取數據 ??byte[] result = br.ReadBytes(len); ??//清空消息池 ??cache.Clear(); ??//講剩余沒處理的消息存入消息池 ??cache.AddRange(br.ReadBytes((int)ms.Length - (int)ms.Position)); ?? ??return result; ?} } |
用戶接受數據代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | using System; using System.Collections.Generic; using System.Net.Sockets; // 表示一個客戶端 public class NetUserToken { ?//連接客戶端的Socket ?public Socket socket; ?//用于存放接收數據 ?public byte[] buffer; ?//每次接受和發送數據的大小 ?private const int size = 1024; ?//接收數據池 ?private List<byte> receiveCache; ?private bool isReceiving; ?//發送數據池 ?private Queue<byte[]> sendCache; ?private bool isSending; ?//接收到消息之后的回調 ?public Action<NetModel> receiveCallBack; ?public NetUserToken() ?{ ??buffer = new byte[size]; ??receiveCache = new List<byte>(); ??sendCache = new Queue<byte[]>(); ?} ?// 服務器接受客戶端發送的消息 ?// < param name="data">Data.< /param> ?public void Receive(byte[] data) ?{ ??UnityEngine.Debug.Log("接收到數據"); ??//將接收到的數據放入數據池中 ??receiveCache.AddRange(data); ??//如果沒在讀數據 ??if(!isReceiving) ??{ ???isReceiving = true; ???ReadData(); ??} ?} ?// 讀取數據 ?private void ReadData() ?{ ??byte[] data = NetEncode.Decode(ref receiveCache); ??//說明數據保存成功 ??if(data != null) ??{ ???NetModel item = NetSerilizer.DeSerialize(data); ???UnityEngine.Debug.Log(item.Message); ???if(receiveCallBack != null) ???{ ????receiveCallBack(item); ???} ???//尾遞歸,繼續讀取數據 ???ReadData(); ??} ??else ??{ ???isReceiving = false; ??} ?} ?// 服務器發送消息給客戶端 ?public void Send() ?{ ??try { ???if (sendCache.Count == 0) { ????isSending = false; ????return; ???} ???byte[] data = sendCache.Dequeue (); ???int count = data.Length / size; ???int len = size; ???for (int i = 0; i < count + 1; i++) { ????if (i == count) { ?????len = data.Length - i * size; ????} ????socket.Send (data, i * size, len, SocketFlags.None); ???} ???UnityEngine.Debug.Log("發送成功!"); ???Send (); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?public void WriteSendDate(byte[] data){ ??sendCache.Enqueue(data); ??if(!isSending) ??{ ???isSending = true; ???Send(); ??} ?} } using System; using System.Collections.Generic; using System.Net.Sockets; ?? // 表示一個客戶端 public class NetUserToken { ?//連接客戶端的Socket ?public Socket socket; ?//用于存放接收數據 ?public byte[] buffer; ?//每次接受和發送數據的大小 ?private const int size = 1024; ?? ?//接收數據池 ?private List<byte> receiveCache; ?private bool isReceiving; ?//發送數據池 ?private Queue<byte[]> sendCache; ?private bool isSending; ?? ?//接收到消息之后的回調 ?public Action<NetModel> receiveCallBack; ?? ?? ?public NetUserToken() ?{ ??buffer = new byte[size]; ??receiveCache = new List<byte>(); ??sendCache = new Queue<byte[]>(); ?} ?? ?// 服務器接受客戶端發送的消息 ?// < param name="data">Data.< /param> ?public void Receive(byte[] data) ?{ ??UnityEngine.Debug.Log("接收到數據"); ??//將接收到的數據放入數據池中 ??receiveCache.AddRange(data); ??//如果沒在讀數據 ??if(!isReceiving) ??{ ???isReceiving = true; ???ReadData(); ??} ?} ?? ?// 讀取數據 ?private void ReadData() ?{ ??byte[] data = NetEncode.Decode(ref receiveCache); ??//說明數據保存成功 ??if(data != null) ??{ ???NetModel item = NetSerilizer.DeSerialize(data); ???UnityEngine.Debug.Log(item.Message); ???if(receiveCallBack != null) ???{ ????receiveCallBack(item); ???} ???//尾遞歸,繼續讀取數據 ???ReadData(); ??} ??else ??{ ???isReceiving = false; ??} ?} ?? ?// 服務器發送消息給客戶端 ?public void Send() ?{ ??try { ???if (sendCache.Count == 0) { ????isSending = false; ????return; ???} ???byte[] data = sendCache.Dequeue (); ???int count = data.Length / size; ???int len = size; ???for (int i = 0; i < count + 1; i++) { ????if (i == count) { ?????len = data.Length - i * size; ????} ????socket.Send (data, i * size, len, SocketFlags.None); ???} ???UnityEngine.Debug.Log("發送成功!"); ???Send (); ??} catch (Exception ex) { ???UnityEngine.Debug.Log(ex.ToString()); ??} ?} ?? ?public void WriteSendDate(byte[] data){ ??sendCache.Enqueue(data); ??if(!isSending) ??{ ???isSending = true; ???Send(); ??} ?} } |
ProtoBuf網絡傳輸到這里就全部完成了。
您可能感興趣的文章:
- python如何通過protobuf實現rpc
- 基于Protobuf C++ serialize到char*的實現方法分析
- 通過Java來測試JSON和Protocol Buffer的傳輸文件大小
- 使用Protocol Buffers的C語言拓展提速Python程序的示例
- Protocol Buffer技術深入理解(C++實例)
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的C#使用Protocol Buffer(ProtoBuf)进行Unity中的Socket通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中try 与catch的使用
- 下一篇: C#通过属性名字符串获取、设置对象属性值