【旧博客转移 - 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很重要,客户端代码加密混淆也很重要。