网络游戏的前后端通讯(二)
【旧博客转移 - 2015年9月20日 22:48】
1.Socket的粘包拆包
tcp协议是有缓冲区的,如果发送的数据太小,会被放置缓冲区里,累积更多的数据再一起发送,发送后不会立马清除缓冲区,等待TCP应答消息到了,才会清除缓冲区。没应答就会继续重发,造成粘包的原因有几种。
- 发送端的多个数据包在缓冲区里一起发送
- 接收端由于网络原因阻塞,然后一次性接受多个包
另外数据包大小比缓冲区大小大的话,会被分成多个片分开发送。
解决以上问题,可以在包的结构设计上处理,一般都采用 包头(4字节)+数据 的结构来封包,包头写入包的长度,下面请看解析包代码
/** * 封包 * **/ private void send(Message vo){ if(vo == null)return; MemoryStream buff = new MemoryStream(); byte[] voByte = Message.encode(vo); byte[] pLen = ByteArrayUtil.writeInt(voByte.Length); buff.Write(pLen, 0, 4);//写包头 buff.Write(voByte, 0, voByte.Length); _socket.Send(buff.ToArray()); } //开一个子线程接受数据 class SocketThread { public Connection conn; private int len = 0; public void run(){ while(true){ if(conn.state != Connection.CONNECTED)continue; if(len == 0){ if(conn._socket.Available < 4)continue;//不足4字节读包长 byte[] lenB = new byte[4]; conn._socket.Receive(lenB, 4, SocketFlags.None); len = ByteArrayUtil.readInt(lenB);//读出包长 } if(conn._socket.Available < len) continue;//不足一个包 byte[] voB = new byte[len]; conn._socket.Receive(voB, len, SocketFlags.None);//把包读出来 len = 0; MemoryStream ms = new MemoryStream(voB); Message vo = Message.decode(ms);//解码包 ms.Close(); Debug.Log("receive:"+vo.getClassName()); conn.resultConnectionHandler(vo); } } }
2.协议加密校验
很多游戏的数据都是明文传输的,别人使用一些抓包工具如Wireshark、WPE等等,就可以轻易修改网络包数据。为了防止数据被修改,我们可以做一下校验,这里介绍一种简单的加key校验方法,服务端跟客户端都用同一种校验算法,根据key生成一个校验值,然后在数据中把这个校验值传输给前端,前端再做校验判断
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace test { class Program { private byte[] datas = new byte[100]; public Program() { //随机生成100个字节的测试数据 Random random = new Random(); for (int i = 0; i < datas.Length; i++) { datas[i] = (byte)random.Next(1, 255); } ArrayToString(datas);//打印数组 //校验密钥 int key = 109; int checkVal = CheckBytes(datas, key);//生成校验值 Console.WriteLine("校验值:" + checkVal); datas[3] = 2;//模拟抓包工具修改数值 Console.WriteLine("模拟抓包工具修改数值:"); ArrayToString(datas);//打印数组 int checkVal2 = CheckBytes(datas, key);//再次生成校验值,此时数据已经在上面被模拟修改了 Console.WriteLine("校验值:" + checkVal2); if (checkVal != checkVal2) { Console.WriteLine("检测到被修改的数据包!"); } } /// /// <summary> /// 根据key校验字节数组 /// </summary> public int CheckBytes(byte[] datas, int key) { int sumCalc = 0; int i = datas.Length; //每一个字节都会影响校验值 while(--i > -1) { sumCalc += (int)(datas[i] ^ key >> datas[i]); } return sumCalc; } public void ArrayToString(byte[] datas) { string str = ""; for (int i = 0; i < datas.Length; i++) { str += datas[i] + ","; } Console.WriteLine(str.Substring(0, str.Length-1)); } static void Main(string[] args) { new Program(); Console.ReadKey(); } } }
运行结果:
不过如果你的客户端代码被别人反编译了,别人知道了怎么获取key以及校验算法后,就能把值修改成能校验通过的数据,所以怎么隐藏key很重要,客户端代码加密混淆也很重要。
转载请注明出处: 李嘉的博客 - http://www.cnblogs.com/lijiajia