网络游戏的前后端通讯(二)

时间:2022-06-01 21:34:02

【旧博客转移 - 2015年9月20日 22:48】

 

1.Socket的粘包拆包

tcp协议是有缓冲区的,如果发送的数据太小,会被放置缓冲区里,累积更多的数据再一起发送,发送后不会立马清除缓冲区,等待TCP应答消息到了,才会清除缓冲区。没应答就会继续重发,造成粘包的原因有几种。

 

  1. 发送端的多个数据包在缓冲区里一起发送
  2. 接收端由于网络原因阻塞,然后一次性接受多个包
另外数据包大小比缓冲区大小大的话,会被分成多个片分开发送。

 

解决以上问题,可以在包的结构设计上处理,一般都采用 包头(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很重要,客户端代码加密混淆也很重要。