网络通讯之Socket-Tcp(二)

网络通讯之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)即可。

 

不懂的小伙伴可自行留言哈,欢迎大家提出批评和建议~

 

张贴在2