二进制序列化
Contents
基础类型(非字符串)
关键类:BitConverter
命名空间:System
主要作用:除字符串的其他常用类型和字节数组互相转换
非字符串类型转二进制
byte[] bytes = BitConverter.GetBytes(1);二进制转非字符串类型
//int类型
int i = BitConverter.ToInt32(bytes);
//重载 从数组的索引读取
i = BitConverter.ToInt32(bytes, index);字符串
关键类:Encoding
命名空间:System.Text
主要作用:将字符串类型和字节数组相互转换,并且决定转换时使用的字符编码类型,网络通信时建议使用UTF-8
字符串转二进制
byte[] bytes = Encoding.UTF8.GetBytes("字符串转字节数组");二进制转字符串
string str = Encoding.UTF8.GetString(bytes);
//重载 指定位置和长度
str = Encoding.UTF8.GetString(bytes, 0, bytes.Length);类对象
自定义规则转换
通过BitConverter和Encoding进行转换
网络通信中我们不能直接使用BinaryFormatter2进制格式化类
因为客户端和服务器使用的语言可能不一样,BinaryFormatter是C#的序列化规则,和其它语言之间的兼容性不好
如果使用它,那么其它语言开发的服务器无法对其进行反序列化,我们可以自定义规则来进行转换。
声明数据结构类
public class PlayerInfo
{
public int lev;//4个字节
public string name;//先存一个int把字符串的长度存进去 再存字符串
public short atk;//2个字节
public bool sex;//1个字节
}类对象转二进制
public byte[] GetBytes(PlayerInfo playerInfo)
{
//得到字节数组容器的容量
int indexNum = sizeof(int) +
sizeof(int) + Encoding.UTF8.GetBytes(playerInfo.name).Length +
sizeof(short) +
sizeof(bool);
//声明字节数组
byte[] playerBytes = new byte[indexNum];
//使用CopyTo方法 将转换的字节数组存入容器中 第二个参数代表索引
int index = 0;
//存入等级
BitConverter.GetBytes(playerInfo.lev).CopyTo(playerBytes,index);
index += sizeof(int);
//存名字
byte[] nameBytes = Encoding.UTF8.GetBytes(playerInfo.name);
BitConverter.GetBytes(nameBytes.Length).CopyTo(playerBytes,index);//存名字长度
index += sizeof(int);
nameBytes.CopyTo(playerBytes,index);//存名字
index += nameBytes.Length;
//存攻击力
BitConverter.GetBytes(playerInfo.atk).CopyTo(playerBytes,index);
index += sizeof(short);
//存性别
BitConverter.GetBytes(playerInfo.sex).CopyTo(playerBytes,index);
return playerBytes;
}二进制转类对象
public byte[] GetPlayerInfo(byte[] playerBytes)
{
PlayerInfo playerInfo = new();
//定义索引
int index = 0;
//获取等级
playerInfo.lev = BitConverter.ToInt32(playerBytes, index);
index += sizeof(int);
//获取名字
//先取长度
int length = BitConverter.ToInt32(playerBytes, index);
index += sizeof(int);
//再取名字
playerInfo.name = Encoding.UTF8.GetString(playerBytes, index, length);
index += length;
//获取攻击力
playerInfo.atk = BitConverter.ToInt16(playerBytes, index);
index += sizeof(short);
//取性别
playerInfo.sex = BitConverter.ToBoolean(index);
return playerInfo;
}BinaryFormatter序列化类
BinaryFormatter是C#提供的序列化类,可以很方便的将类对象序列化成二进制
声明数据结构类
[System.Serializable]
//通过`BinaryFormatter`序列化类时 需要为该类添加[System.Serializable]特性
public class Person
{
public int age = 1;
public string name = "路飞";
public int[] ints = new int[] { 1, 2, 3, 4, 5 };
public List<int> list = new List<int>() { 1, 2, 3, 4 };
public Dictionary<int, string> dic = new Dictionary<int, string>() { { 1,"123"},{ 2,"1223"},{ 3,"435345" } };
public StructTest st = new StructTest(2, "123");
public ClssTest ct = new ClssTest();
}类对象转二进制
使用内存流存储
Person p = new Person();
using (MemoryStream ms = new MemoryStream())
{
//2进制格式化程序
BinaryFormatter bf = new BinaryFormatter();
//序列化对象 生成2进制字节数组 写入到内存流当中
bf.Serialize(ms, p);
//得到对象的2进制字节数组
byte[] bytes = ms.GetBuffer();
//存储字节
File.WriteAllBytes(Application.dataPath + "/Test.test", bytes);
//关闭内存流
ms.Close();
}使用文件流存储
Person p = new Person();
using (FileStream fs = new FileStream(Application.dataPath + "/Test.test", FileMode.OpenOrCreate, FileAccess.Write))
{
//2进制格式化程序
BinaryFormatter bf = new BinaryFormatter();
//序列化对象 生成2进制字节数组 写入到文件流当中
bf.Serialize(fs, p);
fs.Flush();
fs.Close();
}二进制转类对象
使用文件流打开2进制数据文件
using (FileStream fs = File.Open(Application.dataPath + "/Test.test", FileMode.Open, FileAccess.Read))
{
//申明一个 2进制格式化类
BinaryFormatter bf = new BinaryFormatter();
//反序列化
Person p = bf.Deserialize(fs) as Person;
fs.Close();
}使用网络传输过来的2进制数据
//目前没有网络传输 我们还是直接从文件中获取
byte[] bytes = File.ReadAllBytes(Application.dataPath + "/Test.test");
//申明内存流对象 一开始就把字节数组传输进去
using (MemoryStream ms = new MemoryStream(bytes))
{
//申明一个 2进制格式化类
BinaryFormatter bf = new BinaryFormatter();
//反序列化
Person p = bf.Deserialize(ms) as Person;
ms.Close();
}加密存储
何时加密?何时解密?
当我们将类对象转换为2进制数据时进行加密
当我们将2进制数据转换为类对象时进行解密
常用加密算法
- MD5算法
- SHA1算法
- HMAC算法
- AES/DES/3DES算法等
用简单的异或加密
Person p = new Person();
byte key = 199;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, p);
byte[] bytes = ms.GetBuffer();
//异或加密
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] ^= key;
}
File.WriteAllBytes(Application.dataPath + "/Test.test", bytes);
}
//解密
byte[] bytes2 = File.ReadAllBytes(Application.dataPath + "/Test.test");
for (int i = 0; i < bytes2.Length; i++)
{
bytes2[i] ^= key;
}
using (MemoryStream ms = new MemoryStream(bytes2))
{
BinaryFormatter bf = new BinaryFormatter();
Person p2 = bf.Deserialize(ms) as Person;
ms.Close();
}封装序列化管理类
public class BinaryDataMgr
{
//单例
private static readonly Lazy<BinaryDataMgr> instance = new(() => new BinaryDataMgr());
public static BinaryDataMgr Instance => instance.Value;
private BinaryDataMgr(){}
/// <summary>
/// 数据存储的位置
/// </summary>
private static string SAVE_PATH = Application.persistentDataPath + "/Data/";
/// <summary>
/// 存储类对象数据
/// </summary>
/// <param name="obj"></param>
/// <param name="fileName"></param>
public void Save(object obj, string fileName)
{
//先判断路径文件夹有没有
if (!Directory.Exists(SAVE_PATH))
Directory.CreateDirectory(SAVE_PATH);
using (FileStream fs = new FileStream(SAVE_PATH + fileName + ".data", FileMode.OpenOrCreate, FileAccess.Write))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, obj);
fs.Close();
}
}
/// <summary>
/// 读取2进制数据转换成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <returns></returns>
public T Load<T>(string fileName) where T:class
{
//如果不存在这个文件 就直接返回泛型对象的默认值
if( !File.Exists(SAVE_PATH + fileName + ".data") )
return default(T);
T obj;
using (FileStream fs = File.Open(SAVE_PATH + fileName + ".data", FileMode.Open, FileAccess.Read))
{
BinaryFormatter bf = new BinaryFormatter();
obj = bf.Deserialize(fs) as T;
fs.Close();
}
return obj;
}
}