網路通訊之Socket-Tcp(二)

2022-07-09 18:01:59

網路通訊之Socket-Tcp  分成2部分講解:

網路通訊之Socket-Tcp(一):

1.如何理解Socket

2.Socket通訊重要函數

3.Socket Tcp 呼叫的基本流程圖

4.簡單Socket範例

 

網路通訊之Socket-Tcp(二):

1.完善Socket範例【黏包拆包 收發資料】

2.優化Socket

3.Socket網路安全

 

黏包 拆包需要明白的概念:

使用者端給伺服器發(協定)訊息,tcp是位元組流的傳輸方式,所以我們給伺服器發的訊息 都需要轉化為byte[]陣列(包體或訊息體)。

為了能夠區分一個完整的訊息,給伺服器發封包的時候,我們會把 訊息體的長度(簡稱包頭) 也寫入記憶體流,這樣我們就可以根據 包頭的大小 來確定 從記憶體流中讀取多少大小的訊息體

給伺服器發的是這樣的 封包。

網路安全(通訊安全):大家可行根據專案需求是否需要。

對訊息體進行 壓縮、互斥或加密、 crc校驗、保證訊息不被破解 更改。封裝封包之後形成 新封包 。

經過封裝之後 ,給伺服器發的是 新封包。

Socket優化(通訊優化):程式碼沒實現,大家可自行實現

長時間的頻繁收發包,導致手機網路卡發熱,為了防止這種現象,策略是 小包合大包,分幀處理。

 

範例上圖:

使用者端給伺服器傳送一個 趙不灰,伺服器給使用者端 回一個趙老三的訊息。

按 A鍵 連線伺服器,按 S鍵 傳送訊息給伺服器。

 

 

 先看伺服器程式碼:

 主Program.cs

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.Threading;
 5 
 6 namespace ZhaoBuHui.GateWayServer
 7 {
 8     public sealed class ServerConfig
 9     {
10         public static string ip = "192.168.124.2";
11         public static int point = 8082;
12     }
13 
14     class Program
15     {
16         private static Socket m_ListenSocket;
17         static void Main(string[] args)
18         {
19             Console.WriteLine("Hello World!");
20             StartListen();
21         }
22         public static void StartListen()
23         {
24             m_ListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
25             m_ListenSocket.Bind(new IPEndPoint(IPAddress.Parse(ServerConfig.ip), ServerConfig.point));
26             m_ListenSocket.Listen(100);
27             Console.WriteLine("啟動監聽{0}成功", m_ListenSocket.LocalEndPoint.ToString());
28             Thread thread = new Thread(ListenClientConnect);
29             thread.Start();
30         }
31         /// <summary>
32         /// 監聽使用者端連結
33         /// </summary>
34         /// <param name="obj"></param>
35         private static void ListenClientConnect(object obj)
36         {
37             while (true)
38             {
39                 try
40                 {
41                     Socket m_ClientSocket = m_ListenSocket.Accept();
42                     IPEndPoint iPEndPoint = (IPEndPoint)m_ClientSocket.RemoteEndPoint;
43                     Console.WriteLine("收到使用者端IP={0},Port={1}已經連線", iPEndPoint.Address.ToString(), iPEndPoint.Port.ToString());
44                     PlayerClientSocket playerClientSocket = new PlayerClientSocket(m_ClientSocket);
45                     new PlayerInfo(playerClientSocket);
46                 }
47                 catch (Exception ex)
48                 {
49                     Console.WriteLine(ex.ToString());
50                 }
51             }
52         }
53     }
54 }

1.每連線進來一個使用者端,就會返回一個clientsocket,此clientsocket 負責與使用者端的socket 通訊。【socket tcp的特性是 對等】,因此每連線進來我們就會建立一個 PlayerClientSocket。PlayerClientSocket需要有一個Manager 進行管理,感興趣的同學可以自行實現。

 

PlayerClientSocket.cs  玩家使用者端socket 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Net.Sockets;
  4 
  5 namespace ZhaoBuHui.GateWayServer
  6 {
  7     //玩家使用者端socket
  8     public class PlayerClientSocket
  9     {
 10         private Socket m_Socket;
 11 
 12         /// <summary>
 13         /// 接收資料快取區
 14         /// </summary>
 15         private byte[] m_Receive = new byte[1024];
 16         private TestMemoryStreamUtil m_ReceiveMS = new TestMemoryStreamUtil();
 17 
 18         //傳送資料佇列
 19         private Queue<byte[]> m_SendQueue = new Queue<byte[]>();
 20         //壓縮閾值
 21         private const int m_CompressLen = 200;//255也行 這個自定義 
 22         public PlayerClientSocket(Socket socket)
 23         {
 24             m_Socket = socket;
 25             ReceiveMsg();
 26         }
 27 
 28         //傳送訊息
 29         public void SendMsg(ushort protoId, byte[] data)
 30         {
 31             lock (m_SendQueue)
 32             {
 33                 m_SendQueue.Enqueue(PackageData(protoId, data));
 34             }
 35             BeginSendMsg();
 36         }
 37 
 38         //封裝資料【網路安全:壓縮(優化)、加密、crc校驗】
 39         private byte[] PackageData(ushort protoId, byte[] data)
 40         {
 41             bool bCompress = data.Length > m_CompressLen;
 42             //壓縮
 43             if (bCompress) data = ZlibHelper.CompressBytes(data);
 44             //加密
 45             data = SecurityUtil.Xor(data);
 46             //Crc16
 47             ushort crc = Crc16.CalculateCrc16(data);
 48             TestMemoryStreamUtil ms = new TestMemoryStreamUtil();
 49             ms.WriteUShort((ushort)(data.Length + 5));//寫入長度 壓縮1位元組 crc2位元組 協定號2位元組
 50             ms.WriteBool(bCompress);//寫入壓縮
 51             ms.WriteUShort(crc);//寫入crc
 52             ms.WriteUShort(protoId);//寫入協定號
 53             ms.Write(data, 0, data.Length);//寫入data
 54             return ms.ToArray();
 55         }
 56 
 57         private void BeginSendMsg()
 58         {
 59             while (true)
 60             {
 61                 lock (m_SendQueue)
 62                 {
 63                     if (m_SendQueue.Count <= 0) break; ;
 64                     byte[] data = m_SendQueue.Dequeue();
 65                     m_Socket.BeginSend(data, 0, data.Length, SocketFlags.None, SendCallBack, m_Socket);
 66                 }
 67             }
 68         }
 69 
 70         private void SendCallBack(IAsyncResult ar)
 71         {
 72             try
 73             {
 74                 if (!ar.CompletedSynchronously) return;
 75                 m_Socket.EndSend(ar);
 76             }
 77             catch (Exception ex)
 78             {
 79                 Console.WriteLine(ex.ToString());
 80             }
 81         }
 82 
 83         //接收訊息
 84         private void ReceiveMsg()
 85         {
 86             try
 87             {
 88                 //開始接收
 89                 m_Socket.BeginReceive(m_Receive, 0, m_Receive.Length, SocketFlags.None, ReceiveCallBack, m_Socket);
 90             }
 91             catch (Exception ex)
 92             {
 93                 Console.WriteLine(ex.ToString());
 94             }
 95         }
 96 
 97         private void ReceiveCallBack(IAsyncResult ar)
 98         {
 99             try
100             {
101                 int len = m_Socket.EndReceive(ar);
102                 if (len > 0)
103                 {
104                     m_ReceiveMS.Position = m_ReceiveMS.Length;
105                     m_ReceiveMS.Write(m_Receive, 0, len);
106                     while (true)
107                     {
108                         //不完整包過來
109                         if (len > 2)
110                         {
111                             m_ReceiveMS.Position = 0;
112                             ushort currMsglen = m_ReceiveMS.ReadUShort();//當前包體的長度(壓縮 crc 協定號 資料)
113                             ushort currFullLen = (ushort)(currMsglen + 2);//包體+包頭
114                             //過來一個完整包
115                             if (len >= currFullLen)
116                             {
117                                 m_ReceiveMS.Position = 2;
118                                 byte[] currFullData = new byte[currMsglen];
119                                 m_ReceiveMS.Read(currFullData, 0, currMsglen);
120                                 //解封資料
121                                 currFullData = UnBlockData(currFullData, out ushort protoId);
122                                 if (currFullData == null) continue;
123                                 TestCommonEvent.Dispatch(protoId, currFullData);
124                                 //處理剩餘位元組
125                                 if (len - currFullLen > 0)
126                                 {
127                                     byte[] residueData = new byte[len - currFullLen];
128                                     m_ReceiveMS.Position = currFullLen;
129                                     m_ReceiveMS.Read(residueData, 0, len - currFullLen);
130 
131                                     m_ReceiveMS.SetLength(0);
132                                     m_ReceiveMS.Position = 0;
133                                     m_ReceiveMS.Write(residueData, 0, residueData.Length);
134                                     residueData = null;
135                                 }
136                                 else
137                                 {
138                                     m_ReceiveMS.SetLength(0);
139                                     break;
140                                 }
141                             }
142                             else
143                             {
144                                 break; //沒有收到一個完整的包 等待下一次處理
145                             }
146                         }
147                         else
148                         {
149                             break;//還沒收到資料
150                         }
151                     }
152                     ReceiveMsg();
153                 }
154                 else
155                 {
156                     Console.WriteLine("伺服器斷開連結");
157                 }
158             }
159             catch (Exception)
160             {
161                 Console.WriteLine("伺服器斷開連結");
162             }
163         }
164 
165         //解封資料需要跟封裝資料順序一致 否則拿不到正確資料
166         private byte[] UnBlockData(byte[] data, out ushort protoId)
167         {
168             TestMemoryStreamUtil ms = new TestMemoryStreamUtil();
169             ms.SetLength(0);
170             ms.Write(data, 0, data.Length);
171             ms.Position = 0;
172             bool isCompress = ms.ReadBool();
173             ushort crc = ms.ReadUShort();
174             protoId = ms.ReadUShort();
175             ms.Position = 5;
176             data = new byte[data.Length - 5];//-5是因為 壓縮1位元組 crc2位元組 協定號2位元組 拿到的是真正訊息的長度
177             ms.Read(data, 0, data.Length);//加密資料
178             ushort createCrc = Crc16.CalculateCrc16(data);
179             if (createCrc != crc)
180             {
181                 Console.WriteLine("CRC Fail!");
182                 return null;
183             }
184             data = SecurityUtil.Xor(data);//拿到壓縮之後的資料
185             if (isCompress)
186             {
187                 data = ZlibHelper.DeCompressBytes(data);//解壓 原始資料
188             }
189             ms.Dispose();
190             return data;
191         }
192     }
193 }

注意:封裝資料 和解封資料 寫入 讀取順序要一致,否則拿不到正確資料。前後端也必須一致。包括 互斥或加密演演算法 、crc16。

TestCommonEvent  不知道的請點選

 

 協定id類:

 1 public class TestCommonEventId
 2 {
 3     //事件
 4     public const ushort _playerInfo = 10001;
 5 }
 6 
 7 
 8 public class TestCommonProtoId
 9 {
10     //協定
11     public const ushort test1 = 20001;
12 }

 

 測試的 PlayerInfo.cs 

 1 using Google.Protobuf;
 2 using System;
 3 
 4 namespace ZhaoBuHui.GateWayServer
 5 {
 6     class PlayerInfo : IDisposable
 7     {
 8         PlayerClientSocket playerClientSocket;
 9         public PlayerInfo(PlayerClientSocket clientSocket)
10         {
11             playerClientSocket = clientSocket;
12             TestCommonEvent.AddEventListener(TestCommonProtoId.test1, Test1CallBack);
13         }
14 
15         public void Dispose()
16         {
17             TestCommonEvent.RemoveEventListener(TestCommonProtoId.test1, Test1CallBack);
18         }
19 
20         private void Test1CallBack(object obj)
21         {
22             test1 protoMsg = test1.Parser.ParseFrom((byte[])obj);
23             string name = protoMsg.Name;
24             int age = protoMsg.Age;
25             string Sex = protoMsg.Sex;
26             Console.WriteLine(string.Format("name = {0},age={1},Sex ={2}", name, age, Sex));
27 
28             test1 proto = new test1
29             {
30                 Age = new Random().Next(999, 1999),
31                 Sex = "boy",
32                 Name = "趙老三"
33             };
34             playerClientSocket.SendMsg(TestCommonProtoId.test1, proto.ToByteArray());
35 
36         }
37     }
38 }
監聽使用者端發過來的訊息,列印出來,然後又給使用者端 回了一個訊息。
test1: 用google protobuf 生成的c# 程式碼,不知道的請點選

----------------------------以下是使用者端-------------------------------
使用者端程式碼:和伺服器基本一樣,寫好一個 複製貼上過來就可以了。
TestSocketTcpRoutine.cs  socketTcp存取器
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Net;
  4 using System.Net.Sockets;
  5 
  6 public class TestSocketTcpRoutine
  7 {
  8     private Socket m_ClientSocket;
  9 
 10     // 是否連線過socket
 11     private bool m_bDoConnect;
 12     // 是否連線成功
 13     private bool m_IsConnectSuccess;
 14     private Action<bool> m_ConnectCompletedHander;
 15 
 16     /// <summary>
 17     /// 接收資料快取區
 18     /// </summary>
 19     private byte[] m_Receive = new byte[1024];
 20     private TestMemoryStreamUtil m_ReceiveMS = new TestMemoryStreamUtil();
 21     private TestCommonEvent m_CommonEvent = new TestCommonEvent();
 22 
 23     //傳送資料佇列
 24     private Queue<byte[]> m_SendQueue = new Queue<byte[]>();
 25     private TestMemoryStreamUtil m_SendMS = new TestMemoryStreamUtil();
 26     //壓縮閾值
 27     private const int m_CompressLen = 200;//255也行 這個自定義 
 28 
 29 
 30     public void OnUpdate()
 31     {
 32         if (m_bDoConnect)
 33         {
 34             m_bDoConnect = false;
 35             m_ConnectCompletedHander?.Invoke(m_IsConnectSuccess);
 36         }
 37         if (!m_IsConnectSuccess) return;
 38         BeginSendMsg();
 39     }
 40 
 41     //傳送訊息
 42     public void SendMsg(ushort protoId, byte[] data)
 43     {
 44         lock (m_SendQueue)
 45         {
 46             m_SendQueue.Enqueue(PackageData(protoId, data));
 47         }
 48     }
 49     //封裝資料【網路安全:壓縮(優化)、加密、crc校驗】
 50     private byte[] PackageData(ushort protoId, byte[] data)
 51     {
 52         bool bCompress = data.Length > m_CompressLen;
 53         //壓縮
 54         if (bCompress) data = ZlibHelper.CompressBytes(data);
 55         //加密
 56         data = SecurityUtil.Xor(data);
 57         //crc
 58         ushort crc = Crc16.CalculateCrc16(data);
 59         TestMemoryStreamUtil ms = new TestMemoryStreamUtil();
 60         ms.SetLength(0);
 61         ms.WriteUShort((ushort)(data.Length + 5));//寫入長度 壓縮1位元組 crc2位元組 協定號2位元組
 62         ms.WriteBool(bCompress);//寫入壓縮
 63         ms.WriteUShort(crc);//寫入crc
 64         ms.WriteUShort(protoId);//寫入協定號
 65         ms.Write(data, 0, data.Length);//寫入data
 66         return ms.ToArray();
 67     }
 68 
 69     //開始傳送訊息
 70     private void BeginSendMsg()
 71     {
 72         while (true)
 73         {
 74             lock (m_SendQueue)
 75             {
 76                 if (m_SendQueue.Count <= 0) break; ;
 77                 byte[] data = m_SendQueue.Dequeue();
 78                 m_ClientSocket.BeginSend(data, 0, data.Length, SocketFlags.None, SendCallBack, m_ClientSocket);
 79             }
 80         }
 81     }
 82 
 83     //傳送回撥
 84     private void SendCallBack(IAsyncResult ar)
 85     {
 86         try
 87         {
 88             if (!ar.CompletedSynchronously) return;
 89             m_ClientSocket.EndSend(ar);
 90         }
 91         catch (Exception ex)
 92         {
 93             Console.WriteLine(ex.ToString());
 94         }
 95     }
 96 
 97     //連線socket伺服器
 98     public void Connect(string ip, int point, Action<bool> bConnectComplete)
 99     {
100         m_ConnectCompletedHander = bConnectComplete;
101         if ((m_ClientSocket != null && m_ClientSocket.Connected) || m_IsConnectSuccess) return;
102         m_IsConnectSuccess = false;
103         try
104         {
105             m_ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
106             m_ClientSocket.BeginConnect(new IPEndPoint(IPAddress.Parse(ip), point), ConnectCallBack, m_ClientSocket);
107         }
108         catch (Exception ex)
109         {
110             m_ConnectCompletedHander?.Invoke(m_IsConnectSuccess);
111             UnityEngine.Debug.LogError(ex.ToString());
112         }
113     }
114 
115     //連線回撥
116     private void ConnectCallBack(IAsyncResult ar)
117     {
118         m_bDoConnect = true;
119         if (m_ClientSocket.Connected)
120         {
121             ReceiveMsg();
122             m_IsConnectSuccess = true;
123         }
124         else
125         {
126             m_IsConnectSuccess = false;
127             UnityEngine.Debug.LogError("伺服器斷開連結");
128             Dispose();
129         }
130         m_ClientSocket.EndConnect(ar);
131     }
132 
133     //接收訊息
134     private void ReceiveMsg()
135     {
136         try
137         {
138             //開始接收
139             m_ClientSocket.BeginReceive(m_Receive, 0, m_Receive.Length, SocketFlags.None, ReceiveCallBack, m_ClientSocket);
140         }
141         catch (Exception ex)
142         {
143             UnityEngine.Debug.LogError(ex.ToString());
144         }
145     }
146     //接收回撥
147     private void ReceiveCallBack(IAsyncResult ar)
148     {
149         try
150         {
151             int len = m_ClientSocket.EndReceive(ar);
152             if (len > 0)
153             {
154                 m_ReceiveMS.Position = m_ReceiveMS.Length;
155                 m_ReceiveMS.Write(m_Receive, 0, len);
156                 while (true)
157                 {
158                     //不完整包過來
159                     if (len > 2)
160                     {
161                         m_ReceiveMS.Position = 0;
162                         ushort currMsglen = m_ReceiveMS.ReadUShort();//當前包體的長度(壓縮 crc 協定號 資料)
163                         ushort currFullLen = (ushort)(currMsglen + 2);//包體+包頭
164                         //過來一個完整包
165                         if (len >= currFullLen)
166                         {
167                             m_ReceiveMS.Position = 2;
168                             byte[] currFullData = new byte[currMsglen];
169                             m_ReceiveMS.Read(currFullData, 0, currMsglen);
170                             //解封資料
171                             currFullData = UnBlockData(currFullData, out ushort protoId);
172                             if (currFullData == null) continue;
173                             //派發訊息
174                             TestGameEntry.EventMgr.commonEvent.Dispatch(protoId, currFullData);
175                             //處理剩餘位元組
176                             if (len - currFullLen > 0)
177                             {
178                                 byte[] residueData = new byte[len - currFullLen];
179                                 m_ReceiveMS.Position = currFullLen;
180                                 m_ReceiveMS.Read(residueData, 0, len - currFullLen);
181 
182                                 m_ReceiveMS.SetLength(0);
183                                 m_ReceiveMS.Position = 0;
184                                 m_ReceiveMS.Write(residueData, 0, residueData.Length);
185                                 residueData = null;
186                             }
187                             else
188                             {
189                                 m_ReceiveMS.SetLength(0);
190                                 break;
191                             }
192                         }
193                         else
194                         {
195                             break; //沒有收到一個完整的包 等待下一次處理
196                         }
197                     }
198                     else
199                     {
200                         break;//還沒收到資料
201                     }
202                 }
203                 ReceiveMsg();//遞迴迴圈接收
204             }
205             else
206             {
207                 UnityEngine.Debug.LogError("伺服器斷開連結");
208                 Dispose();
209             }
210         }
211         catch (Exception)
212         {
213             UnityEngine.Debug.LogError("伺服器斷開連結");
214             Dispose();
215         }
216     }
217 
218     //解封資料需要跟封裝資料順序一致 否則拿不到正確資料
219     private byte[] UnBlockData(byte[] data, out ushort protoId)
220     {
221         TestMemoryStreamUtil ms = new TestMemoryStreamUtil();
222         ms.SetLength(0);
223         ms.Write(data, 0, data.Length);
224         ms.Position = 0;
225         bool isCompress = ms.ReadBool();
226         ushort crc = ms.ReadUShort();
227         protoId = ms.ReadUShort();
228         ms.Position = 5;
229         data = new byte[data.Length - 5];//-5是因為 壓縮1位元組 crc2位元組 協定號2位元組 拿到的是真正訊息的長度
230         ms.Read(data, 0, data.Length);//加密資料
231         ushort createCrc = Crc16.CalculateCrc16(data);
232         if (createCrc != crc)
233         {
234             UnityEngine.Debug.LogError("CRC Fail!");
235             return null;
236         }
237         data = SecurityUtil.Xor(data);//拿到壓縮之後的資料
238         if (isCompress)
239         {
240             data = ZlibHelper.DeCompressBytes(data);//解壓 原始資料
241         }
242         ms.Dispose();
243         return data;
244     }
245 
246     public void Dispose()
247     {
248         m_bDoConnect = false;
249         m_IsConnectSuccess = false;
250         m_SendQueue.Clear();
251     }
252 }

TestSocketManager.cs   不變,不知道的請點選

 

TestSocket.cs  測試程式碼

 1 using Google.Protobuf;
 2 using UnityEngine;
 3 
 4 public class TestSocket : MonoBehaviour
 5 {
 6     void Start()
 7     {
 8         TestGameEntry.EventMgr.commonEvent.AddEventListener(TestCommonProtoId.test1, Test1CallBack);
 9     }
10 
11     private void OnDestroy()
12     {
13         TestGameEntry.EventMgr.commonEvent.RemoveEventListener(TestCommonProtoId.test1, Test1CallBack);
14     }
15 
16     private void Test1CallBack(object obj)
17     {
18         test1 protoMsg = test1.Parser.ParseFrom((byte[])obj);
19         string name = protoMsg.Name;
20         int age = protoMsg.Age;
21         string Sex = protoMsg.Sex;
22         UnityEngine.Debug.Log(string.Format("name = {0},age={1},Sex ={2}", name, age, Sex));
23     }
24 
25     bool m_isConnectSuccess;
26     void Update()
27     {
28         if (Input.GetKeyDown(KeyCode.A))
29         {
30             TestGameEntry.SocketMgr.Connect("192.168.124.2", 8082, (bool isConnectSuccess) =>
31              {
32                  m_isConnectSuccess = true;
33                  UnityEngine.Debug.Log("連線192.168.124.2:8082" + (isConnectSuccess ? "成功" : "失敗"));
34              });
35         }
36 
37         if (Input.GetKeyDown(KeyCode.S))
38         {
39             if (!m_isConnectSuccess) return;
40             test1 proto = new test1
41             {
42                 Age = Random.Range(1, 100),
43                 Sex = "boy",
44                 Name = "趙不灰"
45             };
46             TestGameEntry.SocketMgr.SendMsg(TestCommonProtoId.test1, proto.ToByteArray());
47         }
48     }
49 }

 

--------------------------------------以下是擴充套件輔助類-------------------------------------------

 Crc16.cs  校驗

 1 public class Crc16
 2 {
 3     // Table of CRC values for high-order byte
 4     private static readonly byte[] _auchCRCHi = new byte[] { 0x01, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x41 };
 5 
 6     // Table of CRC values for low-order byte
 7     private static readonly byte[] _auchCRCLo = new byte[] { 0x01, 0xC1, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x41 };
 8 
 9     /// <summary>
10     /// 獲得CRC16效驗碼
11     /// </summary>
12     /// <param name="buffer"></param>
13     /// <returns></returns>
14     public static ushort CalculateCrc16(byte[] buffer)
15     {
16         byte crcHi = 0xff;  // high crc byte initialized
17         byte crcLo = 0xff;  // low crc byte initialized
18         for (int i = 0; i < buffer.Length; i++)
19         {
20             int crcIndex = crcHi ^ buffer[i];
21             // calculate the crc lookup index
22             crcHi = (byte)(crcLo ^ _auchCRCHi[crcIndex]);
23             crcLo = _auchCRCLo[crcIndex];
24         }
25         return (ushort)(crcHi << 8 | crcLo);
26     }
27 }
View Code

 

 SecurityUtil.cs   互斥或加密

 1 public sealed class SecurityUtil
 2 {
 3     #region xorScale 互斥或因子
 4     /// <summary>
 5     /// 互斥或因子
 6     /// </summary>
 7     private static readonly byte[] xorScale = new byte[] { 45, 66, 38, 55, 23, 254, 9, 165, 90, 19, 41, 45, 201, 58, 55, 37, 254, 185, 165, 169, 19, 171 };//.data檔案的xor加解密因子
 8     #endregion
 9 
10     /// <summary>
11     /// 對陣列進行互斥或[]
12     /// </summary>
13     /// <param name="buffer"></param>
14     /// <returns></returns>
15     public static byte[] Xor(byte[] buffer)
16     {
17         int iScaleLen = xorScale.Length;
18         for (int i = 0; i < buffer.Length; i++)
19         {
20             buffer[i] = (byte)(buffer[i] ^ xorScale[i % iScaleLen]);
21         }
22         return buffer;
23     }
24 }
View Code

 

ZlibHelper.cs   壓縮幫助類

  1 using ComponentAce.Compression.Libs.zlib;
  2 using System;
  3 using System.IO;
  4 
  5 /// <summary>
  6 /// 壓縮幫助類
  7 /// </summary>
  8 public class ZlibHelper
  9 {
 10     #region CompressBytes 對原始位元組陣列進行zlib壓縮,得到處理結果位元組陣列
 11     /// <summary>
 12     /// 對原始位元組陣列進行zlib壓縮,得到處理結果位元組陣列
 13     /// </summary>
 14     /// <param name="OrgByte">需要被壓縮的原始Byte陣列資料</param>
 15     /// <param name="CompressRate">壓縮率:預設為zlibConst.Z_DEFAULT_COMPRESSION</param>
 16     /// <returns>壓縮後的位元組陣列,如果出錯則返回null</returns>
 17     public static byte[] CompressBytes(byte[] OrgByte, int CompressRate = zlibConst.Z_BEST_SPEED)
 18     {
 19         if (OrgByte == null) return null;
 20 
 21         using (MemoryStream OrgStream = new MemoryStream(OrgByte))
 22         {
 23             using (MemoryStream CompressedStream = new MemoryStream())
 24             {
 25                 using (ZOutputStream outZStream = new ZOutputStream(CompressedStream, CompressRate))
 26                 {
 27                     try
 28                     {
 29                         CopyStream(OrgStream, outZStream);
 30                         outZStream.finish();//重要!否則結果資料不完整!
 31                         //程式執行到這裡,CompressedStream就是壓縮後的資料
 32                         if (CompressedStream == null) return null;
 33 
 34                         return CompressedStream.ToArray();
 35                     }
 36                     catch
 37                     {
 38                         return null;
 39                     }
 40                 }
 41             }
 42         }
 43     }
 44     #endregion
 45 
 46     #region DeCompressBytes 對經過zlib壓縮的資料,進行解密和zlib解壓縮,得到原始位元組陣列
 47     /// <summary>
 48     /// 對經過zlib壓縮的資料,進行解密和zlib解壓縮,得到原始位元組陣列
 49     /// </summary>
 50     /// <param name="CompressedBytes">被壓縮的Byte陣列資料</param>
 51     /// <returns>解壓縮後的位元組陣列,如果出錯則返回null</returns>
 52     public static byte[] DeCompressBytes(byte[] CompressedBytes)
 53     {
 54         if (CompressedBytes == null) return null;
 55 
 56         using (MemoryStream CompressedStream = new MemoryStream(CompressedBytes))
 57         {
 58             using (MemoryStream OrgStream = new MemoryStream())
 59             {
 60                 using (ZOutputStream outZStream = new ZOutputStream(OrgStream))
 61                 {
 62                     try
 63                     {
 64                         //-----------------------
 65                         //解壓縮
 66                         //-----------------------
 67                         CopyStream(CompressedStream, outZStream);
 68                         outZStream.finish();//重要!
 69                         //程式執行到這裡,OrgStream就是解壓縮後的資料
 70 
 71                         if (OrgStream == null)
 72                         {
 73                             return null;
 74                         }
 75                         return OrgStream.ToArray();
 76                     }
 77                     catch
 78                     {
 79                         return null;
 80                     }
 81                 }
 82             }
 83         }
 84     }
 85     #endregion
 86 
 87     #region CompressString 壓縮字串
 88     /// <summary>
 89     /// 壓縮字串
 90     /// </summary>
 91     /// <param name="SourceString">需要被壓縮的字串</param>
 92     /// <returns>壓縮後的字串,如果失敗則返回null</returns>
 93     public static string CompressString(string SourceString, int CompressRate = zlibConst.Z_DEFAULT_COMPRESSION)
 94     {
 95         byte[] byteSource = System.Text.Encoding.UTF8.GetBytes(SourceString);
 96         byte[] byteCompress = CompressBytes(byteSource, CompressRate);
 97         if (byteCompress != null)
 98         {
 99             return Convert.ToBase64String(byteCompress);
100         }
101         else
102         {
103             return null;
104         }
105     }
106     #endregion
107 
108     #region DecompressString 解壓字串
109     /// <summary>
110     /// 解壓字串
111     /// </summary>
112     /// <param name="SourceString">需要被解壓的字串</param>
113     /// <returns>解壓後的字串,如果處所則返回null</returns>
114     public static string DecompressString(string SourceString)
115     {
116         byte[] byteSource = Convert.FromBase64String(SourceString);
117         byte[] byteDecompress = DeCompressBytes(byteSource);
118         if (byteDecompress != null)
119         {
120 
121             return System.Text.Encoding.UTF8.GetString(byteDecompress);
122         }
123         else
124         {
125             return null;
126         }
127     }
128     #endregion
129 
130     #region CopyStream 拷貝流
131     /// <summary>
132     /// 拷貝流
133     /// </summary>
134     /// <param name="input"></param>
135     /// <param name="output"></param>
136     private static void CopyStream(Stream input, Stream output)
137     {
138         byte[] buffer = new byte[2000];
139         int len;
140         while ((len = input.Read(buffer, 0, 2000)) > 0)
141         {
142             output.Write(buffer, 0, len);
143         }
144         output.Flush();
145     }
146     #endregion
147 
148     #region GetStringByGZIPData 將解壓縮過的二進位制資料轉換回字串
149     /// <summary>
150     /// 將解壓縮過的二進位制資料轉換回字串
151     /// </summary>
152     /// <param name="zipData"></param>
153     /// <returns></returns>
154     public static string GetStringByGZIPData(byte[] zipData)
155     {
156         return (string)(System.Text.Encoding.UTF8.GetString(zipData));
157     }
158     #endregion
159 }
View Code

 

原始碼地址:http://www.componentace.com/download/  

 

 自行選擇一個版本,我用的是 ZLIB.NET Free v.1.04 - Free

下載完成之後,解壓,把zlib.net.dll   匯入到unity使用者端

伺服器端則匯入原始碼檔案(source)即可。

 

不懂的小夥伴可自行留言哈,歡迎大家提出批評和建議~