网络通信中传输的数据

        在网络通信中

        我们把想要传递的类对象信息序列化为2进制数据(一般为byte字节数组)

        再将该2进制数据通过网络传输给远端设备

        远端设备获取到该2进制数据后再将其反序列化为对应的类对象

unity Dictionary 可以序列化吗 unity 序列化和反序列化_网络

 序列化和反序列化

        序列化:

                将类对象信息转换为可保存或传输的格式的过程

        反序列化:

                与序列化相对,将保存或传输过来的格式转换为类对象的过程

数据持久化2进制

        网络通信相关

                1.BitConverter类:主要用于处理各类型和字节数组间的相互转换

                2.Encoding类:主要用于处理字符串类型和字节数组间的相互转换

                3.加密相关:了解2进制数据加密的常用手段和思路

        

        本地持久化知识点:

                1.File类:文件操作类,用于操作文件

                2.FileStream类:文件流,以流的形式进行文件存储读取操作

                3.MemoryStrem:内存流对象

                4.BinaryFormatter:2进制格式化对象

        在网络开发时,我们不会使用BinaryFormatter类来进行数据的序列化和反序列化 因为客户端和服务端的开发语言大多数情况下是不同的 BinaryFormatter类序列化的数据无法兼容其它语言

序列化

        非字符串类型转字节数组

                主要作用:

                        除字符串的其它常用类型和字节数组相互转换

命名空间:using System

        将一个类对象(构造体)转换为二进制

//构建一个构造体存储玩家信息
    public class PlayerInfo
    {
        public int lev;
        public string playerName;
        public short atk;
        public bool sex;
    }

        

                1.明确字节数组的容量(注意:在确定字符串字节长度时要考虑解析时如何处理)

PlayerInfo info = new PlayerInfo();
        info.lev = 10;
        info.playerName = "缘笙箫";
        info.atk = 88;
        info.sex = false;

        //得到的这个info数据,如果想要转换成为字节数组,那么字节数组容器需要的容量就要提前计算出来
        int indexNum = sizeof(int) +
                       sizeof(int) + //这里的int代表的是字符串的长度,因为UTF-8是可以有多个取值的在存储这个数据的时候要将UTF-8的值传入,方便反序列化的时候确定字符串的长度
                       Encoding.UTF8.GetBytes(info.playerName).Length +
                       sizeof(short) +
                       sizeof(bool);

                2.申明一个装载信息的字节数组容器

byte[] playerBytes = new byte[indexNum];

                3.将对象中的所有信息转为字节数组并放入该容器中(可以利用数组中的CopeTo方法转存字节数组)

                CopyTo方法的第二个参数代表,从容器的第几个位置开始存储

int index = 0;     //从 playerBytes数组的第几个位置开始存储数据

        //等级
        BitConverter.GetBytes(info.lev).CopyTo(playerBytes, index);
        index += sizeof(int);           //下标进行移动

        //姓名
        byte[] strBytes = Encoding.UTF8.GetBytes(info.playerName);
        int num = strBytes.Length;
        //存储的是姓名转换成字节数组后 字节数组的长度
        BitConverter.GetBytes(num).CopyTo(playerBytes, index);
        index += sizeof(int);
        //存储字符串的字节数组
        strBytes.CopyTo(playerBytes, index);
        index += num;

        //攻击力
        BitConverter.GetBytes(info.atk).CopyTo(playerBytes, index);
        index += sizeof(short);
        //性别
        BitConverter.GetBytes(info.sex).CopyTo(playerBytes, index);
        index += sizeof(bool);

                 也可以直接写在构造函数内,方便调用,不用每次再编写一次

public class PlayerInfo
{
    public int lev;
    public string name;
    public short atk;
    public bool sex;

    public byte[] GetBytes()
    {
        int indexNum = sizeof(int) + //lev int类型  4
                      sizeof(int) + //代表 name字符串转换成字节数组后 数组的长度 4
                      Encoding.UTF8.GetBytes(name).Length + //字符串具体字节数组的长度
                      sizeof(short) + //atk short类型 2
                      sizeof(bool); //sex bool类型 1

        byte[] playerBytes = new byte[indexNum];
        int index = 0;//从 playerBytes数组中的第几个位置去存储数据

        //等级
        BitConverter.GetBytes(lev).CopyTo(playerBytes, index);
        index += sizeof(int);

        //姓名
        byte[] strBytes = Encoding.UTF8.GetBytes(name);
        int num = strBytes.Length;
        //存储的是姓名转换成字节数组后 字节数组的长度
        BitConverter.GetBytes(num).CopyTo(playerBytes, index);
        index += sizeof(int);
        //存储字符串的字节数组
        strBytes.CopyTo(playerBytes, index);
        index += num;

        //攻击力
        BitConverter.GetBytes(atk).CopyTo(playerBytes, index);
        index += sizeof(short);
        //性别
        BitConverter.GetBytes(sex).CopyTo(playerBytes, index);
        index += sizeof(bool);

        return playerBytes;
    }
}

总结

        对类对象的2进制序列化

                1.BitConverter转换非字符串的类型的变量为字节数组

                2.Encoding.UTF8转换字符串类型的变量为字节数组(注意:为了考虑反序列化,我们在转存2进制,序列化字符串之前,先序列化字符串字节数组的长度)

        转换流程

                1.明确字节数组的容量

                2.申明一个装载信息的字节数组容器

                3.将对象中的所有信息转为字节数组并放入该容器中(利用数组中的CopeTo方法转存字节数组)

        基类(写)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

//抽象类
public abstract class BaseData
{
    /// <summary>
    /// 用于子类重写的虚方法    获取字节数组的容量
    /// </summary>
    /// <returns>获取字节数组的容量</returns>
    public abstract int GetBytesNum();

    /// <summary>
    /// 返回字节数组
    /// </summary>
    /// <returns>字节数组</returns>
    public abstract byte[] Writeing();
   
    /// <summary>
    /// 将整短形数据转化为二进制并且存入字节数组当中
    /// </summary>
    /// <param name="bytes">字节数组</param>
    /// <param name="shortData">需要存入的整短型</param>
    /// <param name="index">下标编号,ref代表数据在内部进行改变了,外面也跟在改变</param>
    protected  void WriteShort(byte[] bytes,short shortData,ref int index)
    {
        BitConverter.GetBytes(shortData).CopyTo(bytes, index);
        index += sizeof(short);
    }

    protected  void WriteData(byte[] bytes,BaseData Data, ref int index)
    {
        Data.Writeing().CopyTo(bytes, index);
        index += Data.GetBytesNum();
    }

    protected  void WriteInt(byte[] bytes, int IntData, ref int index)
    {
        BitConverter.GetBytes(IntData).CopyTo(bytes, index);
        index += sizeof(int);
    }

    protected  void Writelong(byte[] bytes, short longData, ref int index)
    {
        BitConverter.GetBytes(longData).CopyTo(bytes, index);
        index += sizeof(long);
    }

    protected  void Writestring(byte[] bytes, string stringData, ref int index)
    {
        //先存储string字节数组的长度
        byte[] strBytes = Encoding.UTF8.GetBytes(stringData);
        WriteInt(bytes, strBytes.Length, ref index);
        //BitConverter.GetBytes(strBytes.Length).CopyTo(bytes, index);
        //index += sizeof(int);

        //再存 string字节数组
        strBytes.CopyTo(bytes, index);
        index += strBytes.Length;
    }

    protected  void WriteBool(byte[] bytes,bool boolData, ref int index)
    {
        BitConverter.GetBytes(boolData).CopyTo(bytes, index);
        index += sizeof(bool);
    }
}

        调用(写)

using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

public class PlayerATK : BaseData
{
    public int atk;


    public override int GetBytesNum()
    {
        return sizeof(int);
    }

    public override byte[] Writeing()
    {
        int index = 0;
        byte[] bytes = new byte[GetBytesNum()];

        WriteInt(bytes, atk, ref index);

        return bytes;
    }
}

public class Player : BaseData
{

    public int lev;
    public PlayerATK aTK;
    public string name;

    public override int GetBytesNum()
    {
        return sizeof(int) +
               aTK.GetBytesNum() +
               sizeof(int) +
               Encoding.UTF8.GetBytes(name).Length;
    }

    public override byte[] Writeing()
    {
        int index = 0;
        byte[] bytes = new byte[GetBytesNum()];

        WriteInt(bytes, lev, ref index);
        WriteData(bytes, aTK, ref index);
        Writestring(bytes, name, ref index);

        return bytes;
    }
}

public class Test : MonoBehaviour
{
    
    void Start()
    {
        Player player = new Player();
        player.lev = 1;
        player.aTK = new PlayerATK();
        player.aTK.atk = 4;
        player.name = "gfqig";

        byte[] bytes = player.Writeing();
    }

  
}

反序列化

        字节数组转非字符串类型

                关键类:BitConverter

                所在命名空间:using System

                主要作用:除字符串的其它常用类型和字节数组相互转换

byte[] bytes = BitConverter.GetBytes(99);
        int i = BitConverter.ToInt32(bytes, 0);
        print(i);

unity Dictionary 可以序列化吗 unity 序列化和反序列化_字符串_02

        字节数组转换非字符串类型

                关键类:Encoding        

                所在命名空间:using System.Text

                主要作用:将字符串类型和字节数组相互转换,并且决定转换时使用的字符编码类型,网络通信时建议使用UTF-8

byte[] bytes2 = Encoding.UTF8.GetBytes("yhtgjn");
        string str = Encoding.UTF8.GetString(bytes2, 0, bytes2.Length);
        print(str);

 

unity Dictionary 可以序列化吗 unity 序列化和反序列化_c#_03

 

        将二进制数据转换为一个类对象

                1.获取到对应的字节数组      

PlayerInfo info = new PlayerInfo();
        info.lev = 5;
        info.playerName = "yuan";
        info.atk = 88;
        info.sex = false;


        byte[] playerBytes = info.GetBytes();

                

                2.将字节数组按照序列化时的顺序进行反序列化(将对应字节分组转换为对应类型变量)

PlayerInfo info2 = new PlayerInfo();
        //等级
        int index = 0;
        info2.lev = BitConverter.ToInt32(playerBytes, index);
        index += 4;
        print(info2.lev);
        //姓名的长度
        int length = BitConverter.ToInt32(playerBytes, index);
        index += 4;
        //姓名字符串
        info2.playerName = Encoding.UTF8.GetString(playerBytes, index, length);
        index += length;
        print(info2.playerName);
        //攻击力
        info2.atk = BitConverter.ToInt16(playerBytes, index);
        index += 2;
        print(info2.atk);
        //性别
        info2.sex = BitConverter.ToBoolean(playerBytes, index);
        index += 1;
        print(info2.sex);

unity Dictionary 可以序列化吗 unity 序列化和反序列化_序列化_04

 

总结

        类对象的2进制反序列化

                1.BitConVerter转换字节数组为非字符串的类型的变量

                2.Encoding.UTF8转换字节数组为字符串类型的变量(注意:先读长度,再读字符串)

        转换流程是

                1.获取到对应的字节数组

                2.将字节数组按照序列化时的顺序进行反序列化(将对应字节分组转换为对应类型变量)

        基类(读)

/// <summary>
    /// 反序列化方法,将字节数组信息提取到对象的变量中,读取顺序和序列化时一至
    /// </summary>
    /// <param name="bytes">传入字节数组</param>
    /// <param name="beginIndex">从该字节数组的第几个位置开始解析,默认是0</param>
    /// <returns></returns>
    public abstract int Reading(byte[] bytes,int beginIndex = 0);


    /// <summary>
    /// 读取整数字形
    /// </summary>
    /// <param name="bytes">字节串</param>
    /// <param name="index">下标</param>
    /// <returns>整型</returns>
    protected int ReadInt(byte[] bytes,ref int index)
    {
        int value = BitConverter.ToInt32(bytes, index);
        index += sizeof(int);
        return value;
    }

    protected short ReadShort(byte[] bytes, ref int index)
    {
        short value = BitConverter.ToInt16(bytes, index);
        index += sizeof(short);
        return value;
    }

    protected bool ReadBool(byte[] bytes, ref int index)
    {
        bool value = BitConverter.ToBoolean(bytes, index);
        index += sizeof(bool);
        return value;
    }

    protected string ReadString(byte[] bytes, ref int index)
    {
        //先获取到字符串的长度
        int length = ReadInt(bytes, ref index);
        //index += sizeof(int);

        //再去将字符串给转换出来
        string value = Encoding.UTF8.GetString(bytes, index, length);
        index += length;
        return value;
    } 

    //T代表是泛型,where代表给这个泛型加个范围只能是BaseData里面的类型
    protected T ReadData<T>(byte[] bytes, ref int index) where T:BaseData,new()
    {
        T value = new T();     
        index += value.Reading(bytes,index);
        return value;
    }

        调用(读)

public override int Reading(byte[] bytes, int beginIndex = 0)
    {
        int index = beginIndex;
        atk = ReadInt(bytes, ref index);
        return index - beginIndex;
    }

    public override int Reading(byte[] bytes, int beginIndex = 0)
    {
        int index = beginIndex;
        
        lev = ReadInt(bytes, ref index);
        aTK = ReadData<PlayerATK>(bytes, ref index);
        name = ReadString(bytes, ref index);
        return index - beginIndex;
    }

        Player player2 = new Player();
        player2.Reading(bytes);
        print(player2.lev);
        print(player2.aTK.atk);
        print(player2.name);