1.粘包与拆包相关的概念:
粘包(Packet Pacing)
粘包是指发送方发送的多个小数据包被接收方一次性接收的情况。这可能导致接收方无法正确地区分和处理每个小数据包,从而造成数据处理错误。粘包通常发生在数据发送方迅速发送多个小数据包而接收方未能及时接收和处理的情况下。
拆包(Packet Splitting)
拆包是指发送方发送的一个大数据包被接收方拆分成多个小数据包的情况。这可能导致接收方无法正确地还原原始的大数据包,从而引起数据的不完整性。拆包通常发生在数据发送方发送的数据较大,而接收方缓冲区较小,不能容纳整个数据包的情况下。
消息边界
在数据包中包含消息边界信息,使接收方能够正确地分割和处理每个消息。
消息长度
在数据包中包含消息长度信息,接收方首先读取消息长度,然后根据长度读取相应字节数的数据。
分隔符
在数据包中使用特殊的分隔符标识消息的结束,接收方根据分隔符将数据包拆分成单独的消息。
消息的格式
包体大小四个字节,消息(协议)Id,包体数据(账号,密码,邮箱等等)
4字节:消息大小(描述消息体大小的协议)。
4字节:协议ID(消息类型的唯一标识符)。
size:消息体的大小,以字节为单位。
先将消息的包体数据求出来,根据包体大小(存储空间占4个字节),再求出消息的总长度 = 8 + 包体数据的长度(这里的值8用于考虑接收到的数据包中占用的额外空间,即协议ID(4字节)和消息大小(4字节)),再根据消息的ID对其进行处理,
private void Handle()
{
//粘包指的是在网络通信中,多个小的数据包被合并成一个大的数据包发送到接收方的现象。
//包体大小(4),协议ID(4)包体(byte[])
if (msgLenth >= 8)//接受到的数据的长度
{
byte[] _size = new byte[4];//存放包体里里面的数据
Array.Copy(data, 0, _size, 0, 4);
//"包体大小为 4 字节"是在描述消息体大小所占用的存储空间的长度。
int size = BitConverter.ToInt32(_size, 0);//用于将字节数组表示的整数值转换为一个32位有符号整数。
//_size 数组中存储的字节数据
//本次要拿的长度
var _length = 8 + size;//消息的总长度
if (msgLenth >= _length)
{
//拿出Id
byte[] _id = new byte[4];//存放包体里里面的数据
Array.Copy(data, 4, _id, 0, 4);
//"包体大小为 4 字节"是在描述消息体大小所占用的存储空间的长度。
int id = BitConverter.ToInt32(_id, 0);
//包体
byte[] body = new byte[size];
Array.Copy(data, 8, body, 0, size);
if (msgLenth >= _length)//里面还有数据没拿出来
{
for (int i = 0; i < msgLenth - _length; i++)
{
data[i] = data[_length + i];
}
}
msgLenth -= _length;
Console.WriteLine($"收到客户端请求:{id}");
switch(id)
{
case 1001://注册请求
RigisterMsgHandle(body);
break;
case 1002://登录请求
LoginMsgHandle(body);
break;
case 1003://聊天业务
ChatMsgHandle(body);
break;
}
}
}
}
2.服务端以及客户端发送,接收消息都要按照格式来
//发送消息给服务端
public void SendToServer(int id, string str)
{
Debug.Log("ID:" + id);
//转换成byte[]
var body = Encoding.UTF8.GetBytes(str);
//包体大小(4)消息Id(4)包体内容
byte[] send_buff = new byte[body.Length + 8];
int size = body.Length;
//可以在网络上传输和接收数据,而接收端可以根据协议规定的格式将字节数组转换回整数值来获取原始数据。
var _size = BitConverter.GetBytes(size);
//将整数值转换为字节数组
var _id = BitConverter.GetBytes(id);
Array.Copy(_size, 0, send_buff, 0, 4);
//从 _size 数组中复制了 4 个元素,从索引 0 开始,然后将它们粘贴到 send_buff 数组中,从索引 0 开始。
Array.Copy(_id, 0, send_buff, 4, 4);
Array.Copy(body, 0, send_buff, 8, body.Length);
Client.Instance.Send(send_buff);//发送给服务端
}
//按格式封装后发送消息
public void SendToClient(int id, string str)
{
//转换成byte[]
var body = Encoding.UTF8.GetBytes(str);
//包体大小(4)消息Id(4)包体内容
byte[] send_buff = new byte[body.Length + 8];
int size = body.Length;
//可以在网络上传输和接收数据,而接收端可以根据协议规定的格式将字节数组转换回整数值来获取原始数据。
var _size = BitConverter.GetBytes(size);
//将整数值转换为字节数组
var _id = BitConverter.GetBytes(id);
Array.Copy(_size, 0, send_buff, 0, 4);
//从 _size 数组中复制了 4 个元素,从索引 0 开始,然后将它们粘贴到 send_buff 数组中,从索引 0 开始。
Array.Copy(_id, 0, send_buff, 4, 4);
Array.Copy(body, 0, send_buff, 8, body.Length);
Send(send_buff);//发送给客户端
}