网络游戏数据传输:粘包的处理

时间:2022-06-01 17:11:04
网络传输大体上包含这四个协议type area command message,这四层协议是依次向下传递的。
所谓协议,说简单点就是服务器端和客户端的一个约定,
比如,向服务器发送(1,0,1,“账号:*** 密码***”)
约定type中1代表登录模块,因为登陆不需要经过area协议,所以跳过area协议,直接到command协议,
command中1代表登陆,2代表注册,3代表返回登陆结果,传入的是1,故进行登陆逻辑,注意,如果type不是1,此处1,2,3就会代表别的东西了。
如果登陆成功,服务器会返回(1,0,3,“true”)
如果不嫌麻烦,完全可以只定义一个协议,但是估计没人那么做
在传输过程中,为了进行高效的传输,所有的数据都会连在一起,如果传的是int a = 1 string b = “qaz”传输时会变成1qaz
为了进行区分,通常会在传输数据体前用一个8字节的int传入这个数据体的长度(一般一个int就占8个字节),这个int通常叫做数据头
如果想传的数据占256个字节,那么传的时候会发送256+8个字节
如果想发送一个float和一个bool,那么真正发送的会是连在一起的int float int bool
下面说下如何进行解析
在连接服务器后,用BeginReceive进行数据接收,其中一个参数定义了缓存大小
建立一个字节数组byte[1024]来作为缓存
建立一个ByteArray来存储待处理数据,ByteArray是可以改变大小的
当缓存中收到数据时,会将数据加入到ByteArray
之后,判断是否正在进行数据处理,如果没在则开始数据处理:





判断ByteArray长度是否大于8,即是否把记录消息体长度的int读取完
若小于8,不做处理等待,下次接受
若大于8,使用ReadInt读取Int
ByteArray有一个position属性,默认为0,当执行readint时会从0下标读到8下标,因为int字节长度为8,所以就算ByteArray里有很多数据,就算有1024个字节,readint也只会读8位,之后position=8
举个例子,如果传入 1,2,3三个int 连续执行3次ReadInt就会得到1,2,3
获取数据体长度int L后,再判断剩下的数据长度是否大于L,若大于则说明数据完整,建立新的ArrayList2,从ArrayList读取8到8+L的内容,AL的position+L
对ArrayList2进行Read,至于是ReadInt还是ReadBytes,要按照协议进行,简而言之,服务器传的顺序要和收的顺序一致
之后,我们创建一个新的缓存来存储ListArray position之后的数据,再使ArrayList=这个新缓存

再次执行"↓"之后的内容


相关代码:

 //消息体长度为一个8字节数值 长度不足的时候 说明消息未接收完成 或者是废弃消息
        if (ioBuff.Length < 8) {
            isRead = false;
            return;
        }
        //读取定义的消息的长度
        int dataSize = ioBuff.ReadInt();
        if (dataSize > ioBuff.Length - 8) {
            //消息体长度不够 等待下个消息的到来
            ioBuff.Postion = 0;
            isRead = false;
            return;
        }
        //创建完整消息体的动态数组
        ByteArray ioData = new ByteArray();
        //从消息缓存中取出正确的消息体 字节数组内容
        ioData.WriteBytes(ioBuff.Buffer, 8, dataSize);
        ioBuff.Postion += dataSize;
        int type=ioData.ReadInt();
        int area=ioData.ReadInt();
        int command=ioData.ReadInt();
        byte[] cache=ioData.ReadBytes();
        object message = AceCode.Code.aceDecode(cache);
        //转换为传输模型用于使用
        SocketModel model = new SocketModel(type, area, command, message);
        //将消息存储进消息列表 等待Unity来读取
        messageList.Add(model);
        ////
        //创建新缓存区
        ByteArray bytes = new ByteArray();
        //将旧缓存区的剩余数据移动到新缓存区
        bytes.WriteBytes(ioBuff.Buffer, ioBuff.Postion, ioBuff.Buffer.Length - ioBuff.Postion);
        //更新缓存区
        ioBuff = bytes;
        onData();