所谓KLV即Key-Length-Value,以【键-数据长度-数据】的形式将数据序列化成字节流,
这是一种高性能和兼容性的数据序列化方案,,缺点就是用起来很麻烦,
其出现的需求场景如下:
1,硬件和云端的数据交互,最开始是以流的形式顺序写入数据,但是由于版本迭代,数据字段难免出现新增插入更新移除等现象,流式结构加了一大堆版本判定,混乱不堪
3,于是考虑使用Json来序列化数据,但是json性能消耗以及资源占用不甚理想,而且硬件端也没有现成的Json库使用
4,因此模仿Json搞出了一个KLV格式,为每个数据指定一个key,下位机根据key获取数据,解决兼容性问题
该方案的Length比较特殊,由于传输中存在大量基础数值类型,使用4个字节比较浪费,但是1个字节又肯定不够用,Varint又不适用,因此特使用一种特殊的机制来描述Length:当首字节的最高位为0时,用1个字节,用后7位表示数值;当首字节的最高位为1时,用4个字节,用后31位表示数值
不使用Key-Type-Value是因为KLV更简单灵活,传输的时候不用关心数据是什么,用的时候才会根据文档读取指定的key转成具体的数据,当读取到不认识的Key时,可以直接跳过Length,不存在Type的兼容性问题
测试以及对比:
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KLV { internal class Test { public void Start() { for (int i = 0; i < 5; i++) { AAA(); } } public void AAA() { Entity entity = new Entity(); Stopwatch sw = new Stopwatch(); sw.Start(); KLVWriter kw = WriteEntity(entity); Console.WriteLine("KLV序列化耗时:" + sw.ElapsedTicks); sw.Restart(); Entity entity_read = Read(kw.Data, kw.StartIndex, kw.Length); Console.WriteLine("KLV反序列化耗时:" + sw.ElapsedTicks); sw.Restart(); string json = JsonConvert.SerializeObject(entity); Console.WriteLine("Json序列化耗时:" + sw.ElapsedTicks); sw.Restart(); Entity entity1 = JsonConvert.DeserializeObject<Entity>(json); Console.WriteLine("Json反序列化耗时:" + sw.ElapsedTicks); sw.Restart(); //通过json字符串对比前后是否一致 Console.WriteLine(json == JsonConvert.SerializeObject(entity_read)); } Entity Read(byte[] data, int startIndex, int length) { KLVReader kr = new KLVReader(data, startIndex, length); Entity entity = new Entity { P1 = kr.ReadBoolem(1), P2 = kr.ReadInt8(2), P3 = kr.ReadUInt8(3), P4 = kr.ReadInt16(4), P5 = kr.ReadUInt16(5), P6 = kr.ReadInt32(6), P7 = kr.ReadUInt32(7), P8 = kr.ReadInt64(8), P9 = kr.ReadUInt64(9), P10 = kr.ReadFloat(10), P11 = kr.ReadDouble(11), P12 = kr.ReadString(12), P13 = kr.ReadBoolemArray(13), P14 = kr.ReadInt8Array(14), P15 = kr.ReadUInt8Array(15), P16 = kr.ReadInt16Array(16), P17 = kr.ReadUInt16Array(17), P18 = kr.ReadInt32Array(18), P19 = kr.ReadUInt32Array(19), P20 = kr.ReadInt64Array(20), P21 = kr.ReadUInt64Array(21), P22 = kr.ReadFloatArray(22), P23 = kr.ReadDoubleArray(23), P24 = kr.ReadStringList(24), P25 = kr.ReadObject(25, read => { return new EntitySub { P1 = read.ReadInt32(1), P2 = read.ReadDouble(2), P3 = read.ReadString(3) }; }), P26 = kr.ReadObjectList(26, read => { return new EntitySub { P1 = read.ReadInt32(1), P2 = read.ReadDouble(2), P3 = read.ReadString(3) }; }), P27 = kr.ReadDictionary(27, read => { return (read.ReadInt32(1), read.ReadString(2)); }), P28 = kr.ReadDictionary(28, read => { return (read.ReadString(1), read.ReadInt32(2)); }), }; return entity; } KLVWriter WriteEntity(Entity entity) { byte[] data = new byte[1024]; int offset = 0; KLVWriter kw = new KLVWriter(data, offset); kw.WriteBoolem(1, entity.P1); kw.WriteInt8(2, entity.P2); kw.WriteUInt8(3, entity.P3); kw.WriteInt16(4, entity.P4); kw.WriteUInt16(5, entity.P5); kw.WriteInt32(6, entity.P6); kw.WriteUInt32(7, entity.P7); kw.WriteInt64(8, entity.P8); kw.WriteUInt64(9, entity.P9); kw.WriteFloat(10, entity.P10); kw.WriteDouble(11, entity.P11); kw.WriteString(12, entity.P12); kw.WriteBooleanArray(13, entity.P13); kw.WriteInt8Array(14, entity.P14); kw.WriteUInt8Array(15, entity.P15); kw.WriteInt16Array(16, entity.P16); kw.WriteUInt16Array(17, entity.P17); kw.WriteInt32Array(18, entity.P18); kw.WriteUInt32Array(19, entity.P19); kw.WriteInt64Array(20, entity.P20); kw.WriteUInt64Array(21, entity.P21); kw.WriteFloatArray(22, entity.P22); kw.WriteDoubleArray(23, entity.P23); kw.WriteStringArray(24, entity.P24); kw.WriteObject(25, p => { p.WriteInt32(1, entity.P25.P1); p.WriteDouble(2, entity.P25.P2); p.WriteString(3, entity.P25.P3); }); kw.WriteObjects(26, entity.P26, (p, val) => { p.WriteInt32(1, val.P1); p.WriteDouble(2, val.P2); p.WriteString(3, val.P3); }); kw.WriteObjects(27, entity.P27, (p, val) => { p.WriteInt32(1, val.Key); p.WriteString(2, val.Value); }); kw.WriteObjects(28, entity.P28, (p, val) => { p.WriteString(1, val.Key); p.WriteInt32(2, val.Value); }); return kw; } } }View Code
序列化写入器:
using System; using System.Collections.Generic; using System.Text; namespace KLV { /// <summary> /// 一个KLV写入器 /// </summary> public class KLVWriter { /// <summary> /// 缓冲区 /// </summary> public byte[] Data { get; private set; } /// <summary> /// 当前KLV开始索引 /// </summary> public int StartIndex { get; private set; } /// <summary> /// 当前KLV偏移量 /// </summary> public int Offset { get; private set; } /// <summary> /// 写入数据的长度 /// </summary> public int Length { get { return Offset - StartIndex; } } readonly object _lock = new object(); /// <summary> /// 以传入的字节数组为基础,创建一个KLV写入器,从指定索引写数据 /// </summary> /// <param name="data"></param> /// <param name="startIndex"></param> public KLVWriter(byte[] data, int startIndex) { Data = data; StartIndex = startIndex; Offset = startIndex; } public void Reset(byte[] data, int startIndex) { Data = data; StartIndex = startIndex; Offset = startIndex; } /// <summary> /// 当已知数据长度时,传入长度,自动计算,减少空间浪费 /// </summary> void WriteLength_Auto(int len) { if (len < 0x80) { Data[Offset++] = (byte)len; //最高位为0时,用1个字节中的后7位表示数值 } else { Offset += WriteLength_FourBytes(len, Offset); } } /// <summary> /// 写入固定4字节的长度,当先写数据时,可以通过固定4个字节来提高性能 /// </summary> int WriteLength_FourBytes(int len, int offset) { len = (int)(len | 0x80000000); //最高位为1时,用4个字节中后31位表示数值 len.GetBytes().CopyTo(Data, offset); return 4; } public void WriteBoolem(byte key, bool val) { WriteUInt8(key, (byte)(val ? 1 : 0)); } public void WriteInt8(byte key, sbyte val) { WriteUInt8(key, (byte)val); } public void WriteUInt8(byte key, byte val) { lock (_lock) { Data[Offset++] = key; //key WriteLength_Auto(1); //len Data[Offset++] = val; //val } } void Write(byte key, byte[] bs) { lock (_lock) { Data[Offset++] = key; //key WriteLength_Auto(bs.Length); //len bs.CopyTo(Data, Offset); //val Offset += bs.Length; } } public void WriteInt16(byte key, short val) { Write(key, val.GetBytes()); } public void WriteUInt16(byte key, ushort val) { Write(key, val.GetBytes()); } public void WriteInt32(byte key, int val) { Write(key, val.GetBytes()); } public void WriteUInt32(byte key, uint val) { Write(key, val.GetBytes()); } public void WriteInt64(byte key, long val) { Write(key, val.GetBytes()); } public void WriteUInt64(byte key, ulong val) { Write(key, val.GetBytes()); } public void WriteFloat(byte key, float val) { Write(key, val.GetBytes()); } public void WriteDouble(byte key, double val) { Write(key, val.GetBytes()); } public void WriteString(byte key, string val) { Write(key, Encoding.UTF8.GetBytes(val)); } public void WriteBooleanArray(byte key, IList<bool> vals) { lock (_lock) { Data[Offset++] = key; //key WriteLength_Auto(vals.Count); //len foreach (var val in vals) { //val Data[Offset++] = (byte)(val ? 1 : 0); } } } public void WriteInt8Array(byte key, IList<sbyte> vals) { lock (_lock) { Data[Offset++] = key; WriteLength_Auto(vals.Count); foreach (var val in vals) { Data[Offset++] = (byte)val; } } } public void WriteUInt8Array(byte key, IList<byte> vals) { lock (_lock) { Data[Offset++] = key; //key WriteLength_Auto(vals.Count); //len vals.CopyTo(Data, Offset); //val Offset += vals.Count; } } public void WriteInt16Array(byte key, IList<short> vals) { lock (_lock) { Data[Offset++] = key; //key int size = 2; WriteLength_Auto(vals.Count * size); //len foreach (var val in vals) { //val,为了提高性能,不再采用委托封装 val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteUInt16Array(byte key, IList<ushort> vals) { lock (_lock) { Data[Offset++] = key; int size = 2; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteInt32Array(byte key, IList<int> vals) { lock (_lock) { Data[Offset++] = key; int size = 4; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteUInt32Array(byte key, IList<uint> vals) { lock (_lock) { Data[Offset++] = key; int size = 4; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteInt64Array(byte key, IList<long> vals) { lock (_lock) { Data[Offset++] = key; int size = 8; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteUInt64Array(byte key, IList<ulong> vals) { lock (_lock) { Data[Offset++] = key; int size = 8; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteFloatArray(byte key, IList<float> vals) { lock (_lock) { Data[Offset++] = key; int size = 4; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } public void WriteDoubleArray(byte key, IList<double> vals) { lock (_lock) { Data[Offset++] = key; int size = 8; WriteLength_Auto(vals.Count * size); foreach (var val in vals) { val.GetBytes().CopyTo(Data, Offset); Offset += size; } } } /// <summary> /// 写入一个字符串集合 /// </summary> /// <param name="key"></param> /// <param name="vals"></param> public void WriteStringArray(byte key, IList<string> vals) { lock (_lock) { Data[Offset++] = key; Offset += 4; int startIndex = Offset; //记录数据开始的索引 foreach (var val in vals) { //字符串比较特殊,异于数值集合和对象集合 byte[] bs = Encoding.UTF8.GetBytes(val); WriteLength_Auto(bs.Length); //字符串的长度 bs.CopyTo(Data, Offset); Offset += bs.Length; } WriteLength_FourBytes(Offset - startIndex, startIndex - 4); } } /// <summary> /// 写入一个对象,必须在委托函数中写入,目的是限制写入顺序,避免出现数据混乱 /// </summary> /// <param name="key">对象的key</param> /// <param name="writer">对象写入器</param> public void WriteObject(byte key, Action<KLVWriter> writer) { lock (_lock) { Data[Offset++] = key; var kw = new KLVWriter(Data, Offset + 4); //预留4个字节 writer(kw); //val Offset += WriteLength_FourBytes(kw.Length, Offset); //向预留的4个字节写入数据长度的4个字节 Offset += kw.Length; //数据的长度 } } /// <summary> /// 写入一个对象集,必须是同一个类型的对象,内部会循环回调writer,执行相同的写入操作 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key">对象的key</param> /// <param name="vals">对象集</param> /// <param name="writer">每个对象的写入操作</param> public void WriteObjects<T>(byte key, IEnumerable<T> vals, Action<KLVWriter, T> writer) { lock (_lock) { Data[Offset++] = key; Offset += 4; //预留4个字节写长度的字节大小 int startIndex = Offset; //记录数据开始的索引 KLVWriter kw = new KLVWriter(null, 0); //复用一个写入器对象,提高性能 foreach (var val in vals) { kw.Reset(Data, Offset + 4); //每个对象单独有个数据长度,也要预留 writer(kw, val); Offset += WriteLength_FourBytes(kw.Length, Offset); //长度的字节个数 Offset += kw.Length; //数据的字节个数 } WriteLength_FourBytes(Offset - startIndex, startIndex - 4); } } } }View Code
反序列化读取器:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace KLV { /// <summary> /// KLVV读取器 /// </summary> public class KLVReader { /// <summary> /// 记录数据相关偏移量的小容器 /// </summary> internal class Block { /// <summary> /// 数据在缓冲区中的偏移量 /// </summary> public int Offset; /// <summary> /// 数据的字节长度 /// </summary> public int Length; public Block(int offset, int length) { Offset = offset; Length = length; } } /// <summary> /// 解析出来的数据 /// </summary> readonly Dictionary<byte, Block> dict = new Dictionary<byte, Block>(); /// <summary> /// 缓冲区 /// </summary> byte[] Data; readonly object _lock = new object(); /// <summary> /// 从缓冲区中指定位置开始指定长度中解析一个KLV结构,并提供读取操作 /// </summary> /// <param name="data"></param> /// <param name="startIndex"></param> /// <param name="dataLength"></param> public KLVReader(byte[] data, int startIndex, int dataLength) { Parse(data, startIndex, dataLength); } KLVReader() { } void Parse(byte[] data, int startIndex, int dataLength) { dict.Clear(); Data = data; int offset = startIndex; int offset_end = offset + dataLength; while (offset < offset_end) { //循环解析 byte key = data[offset++]; //先读取一个字节的key (int length, int size) = ReadLenght(data, offset); //在读取len offset += size; try { dict.Add(key, new Block(offset, length)); //最后读取val } catch (Exception ex) { Console.WriteLine(key); throw ex; } offset += length; }; } /// <summary> /// 读取KLV中的len,因为KLV采用了2中方式表示len,因此要特殊处理, /// 当作为len的首字节的最高位为0,则说明用1个字节表示len,这1个字节的后7位表示最多127个数值, /// 当作为len的首字节的最高位为1,则说明用4个字节表示len,这4个字节的后31位标识最多2的31次方的数值 /// </summary> /// <param name="data"></param> /// <param name="offset"></param> /// <returns></returns> (int, int) ReadLenght(byte[] data, int offset) { int size = 1; //默认认为是1个字节,最高位为0,用一个字节的后7位表示,小于0x80 int length = data[offset]; if (length >= 0x80) { //大于等于0x80就认为是用4字节 length = data.ToInt32(offset); length &= 0x7FFFFFFF; //因为最高位固定为1,数值只用了后31位,所以要将最高位置为0 size = 4; } return (length, size); } /// <summary> /// 从字典中查找指定key的相关数据 /// </summary> /// <param name="key"></param> /// <returns></returns> /// <exception cref="KLVException"></exception> Block GetBlock(byte key) { if (!dict.TryGetValue(key, out Block block)) { throw new KLVException($"未找到key:{key},data:{BitConverter.ToString(Data)}"); } if (block.Offset + block.Length >= Data.Length) { throw new KLVException($"Offset:{block.Offset}+Length:{block.Length}超出了缓冲区大小,data:{BitConverter.ToString(Data)}"); } return block; } public bool ReadBoolem(byte key) { lock (_lock) { return Data[GetBlock(key).Offset] == 1; } } public sbyte ReadInt8(byte key) { lock (_lock) { return (sbyte)Data[GetBlock(key).Offset]; } } public byte ReadUInt8(byte key) { lock (_lock) { return Data[GetBlock(key).Offset]; } } public short ReadInt16(byte key) { lock (_lock) { return Data.ToInt16(GetBlock(key).Offset); } } public ushort ReadUInt16(byte key) { lock (_lock) { return Data.ToUInt16(GetBlock(key).Offset); } } public int ReadInt32(byte key) { lock (_lock) { return Data.ToInt32(GetBlock(key).Offset); } } public uint ReadUInt32(byte key) { lock (_lock) { return Data.ToUInt32(GetBlock(key).Offset); } } public long ReadInt64(byte key) { lock (_lock) { return Data.ToInt64(GetBlock(key).Offset); } } public ulong ReadUInt64(byte key) { lock (_lock) { return Data.ToUInt64(GetBlock(key).Offset); } } public float ReadFloat(byte key) { lock (_lock) { return Data.ToFloat(GetBlock(key).Offset); } } public double ReadDouble(byte key) { lock (_lock) { return Data.ToDouble(GetBlock(key).Offset); } } public string ReadString(byte key) { lock (_lock) { var block = GetBlock(key); return Encoding.UTF8.GetString(Data, block.Offset, block.Length); } } public bool[] ReadBoolemArray(byte key) { lock (_lock) { var block = GetBlock(key); bool[] arr = new bool[block.Length]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data[block.Offset + i] == 1; } return arr; } } public sbyte[] ReadInt8Array(byte key) { lock (_lock) { var block = GetBlock(key); sbyte[] arr = new sbyte[block.Length]; for (int i = 0; i < arr.Length; i++) { arr[i] = (sbyte)Data[block.Offset + i]; } return arr; } } public byte[] ReadUInt8Array(byte key) { lock (_lock) { var block = GetBlock(key); byte[] arr = new byte[block.Length]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data[block.Offset + i]; } return arr; } } public short[] ReadInt16Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 2; short[] arr = new short[block.Length / size]; //很多函数这块代码基本一样,但是不用委托封装,稍微提高点性能 for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToInt16(block.Offset + i * size); } return arr; } } public ushort[] ReadUInt16Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 2; ushort[] arr = new ushort[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToUInt16(block.Offset + i * size); } return arr; } } public int[] ReadInt32Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 4; int[] arr = new int[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToInt32(block.Offset + i * size); } return arr; } } public uint[] ReadUInt32Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 4; uint[] arr = new uint[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToUInt32(block.Offset + i * size); } return arr; } } public long[] ReadInt64Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 8; long[] arr = new long[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToInt64(block.Offset + i * size); } return arr; } } public ulong[] ReadUInt64Array(byte key) { lock (_lock) { var block = GetBlock(key); int size = 8; ulong[] arr = new ulong[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToUInt64(block.Offset + i * size); } return arr; } } public float[] ReadFloatArray(byte key) { lock (_lock) { var block = GetBlock(key); int size = 4; float[] arr = new float[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToFloat(block.Offset + i * size); } return arr; } } public double[] ReadDoubleArray(byte key) { lock (_lock) { var block = GetBlock(key); int size = 8; double[] arr = new double[block.Length / size]; for (int i = 0; i < arr.Length; i++) { arr[i] = Data.ToDouble(block.Offset + i * size); } return arr; } } /// <summary> /// 获取解析出来的字符串集合 /// </summary> /// <param name="key">集合的key</param> /// <returns></returns> public IList<string> ReadStringList(byte key) { lock (_lock) { var block = GetBlock(key); List<string> list = new List<string>(); //字符串长度不固定,因此不知道数量,这里使用集合 int offset = block.Offset; int offset_end = offset + block.Length; while (offset < offset_end) { (int len, int size) = ReadLenght(Data, offset); //每个字符串都有一个长度 offset += size; list.Add(Encoding.UTF8.GetString(Data, offset, len)); offset += len; } return list; } } /// <summary> /// 读取对象 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key">对象的key</param> /// <param name="read">对象的读取操作</param> /// <returns></returns> public T ReadObject<T>(byte key, Func<KLVReader, T> read) where T : class { lock (_lock) { var block = GetBlock(key); return read(new KLVReader(Data, block.Offset, block.Length)); } } /// <summary> /// 读取对象集,返回一个枚举器 /// </summary> /// <param name="key">对象集的key</param> /// <param name="read">每个对象的读取操作</param> public IEnumerable<T> ReadObjects<T>(byte key, Func<KLVReader, T> read) { lock (_lock) { var block = GetBlock(key); int offset = block.Offset; int offset_end = offset + block.Length; KLVReader kr = new KLVReader(); while (offset < offset_end) { (int len, int size) = ReadLenght(Data, offset); offset += size; kr.Parse(Data, offset, len); //复用一个kr,提高性能 var obj = read(kr); offset += len; yield return obj; } } } /// <summary> /// 读取对象集 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="read"></param> /// <returns></returns> public List<T> ReadObjectList<T>(byte key, Func<KLVReader, T> read) { List<T> list = new List<T>(); //return ReadObjects(key,read).ToList(); //为了提高少许的性能,不用上面的枚举器 lock (_lock) { var block = GetBlock(key); int offset = block.Offset; int offset_end = offset + block.Length; KLVReader kr = new KLVReader(); while (offset < offset_end) { (int len, int size) = ReadLenght(Data, offset); offset += size; kr.Parse(Data, offset, len); //复用一个kr,提高性能 list.Add(read(kr)); offset += len; } } return list; } /// <summary> /// 读取一个字典 /// </summary> /// <typeparam name="K"></typeparam> /// <typeparam name="V"></typeparam> /// <param name="key"></param> /// <param name="read"></param> /// <returns></returns> public Dictionary<K, V> ReadDictionary<K, V>(byte key, Func<KLVReader, (K, V)> read) { Dictionary<K, V> dict = new Dictionary<K, V>(); //foreach (var kv in ReadObjects(key, read)) { //为了提高少许的性能,不用上面的枚举器 // dict.Add(kv.Item1,kv.Item2); //} lock (_lock) { var block = GetBlock(key); int offset = block.Offset; int offset_end = offset + block.Length; KLVReader kr = new KLVReader(); while (offset < offset_end) { //为了稍微提高性能,不对这块进行封装 (int len, int size) = ReadLenght(Data, offset); offset += size; kr.Parse(Data, offset, len); //复用一个kr,提高性能 var kv = read(kr); dict.Add(kv.Item1, kv.Item2); offset += len; } } return dict; } } }View Code
测试用的模型及其数据:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KLV { internal class Entity { public bool P1 { get; set; } = true; public sbyte P2 { get; set; } = -126; public byte P3 { get; set; } = 255; public short P4 { get; set; } = -1234; public ushort P5 { get; set; } = 2345; public int P6 { get; set; } = -345678; public uint P7 { get; set; } = 45678910; public long P8 { get; set; } = -56789101112; public ulong P9 { get; set; } = 67891011121314; public float P10 { get; set; } = 3.1415f; public double P11 { get; set; } = 2.718281828459045; public string P12 { get; set; } = "测试啊123"; public bool[] P13 { get; set; } = new bool[] { true, false, true }; public sbyte[] P14 { get; set; } = new sbyte[] { -1, 2, -3, 4 }; public byte[] P15 { get; set; } = { 200, 201, 203 }; public short[] P16 { get; set; } = new short[] { -17904, 17905, -17906 }; public ushort[] P17 { get; set; } = new ushort[] { 34567, 45612, 44323 }; public int[] P18 { get; set; } = new int[] { -1234, 4567, -5678 }; public uint[] P19 { get; set; } = new uint[] { 1234567, 7890321, 567431 }; public long[] P20 { get; set; } = new long[] { -44444444, 55555555555, -66666666666 }; public ulong[] P21 { get; set; } = new ulong[] { 888888888, 99999999 }; public float[] P22 { get; set; } = new float[] { 2.34f, -45.1f, 78.4f }; public double[] P23 { get; set; } = new double[] { -123.321, 234.432, -567.897 }; public IList<string> P24 { get; set; } = new string[] { "测试啊123", "测试啊abc", "测试啊+-*" }; public EntitySub P25 { get; set; } = new EntitySub() { P1 = 123, P2 = 1.23, P3 = "子实体类测试123" }; public List<EntitySub> P26 { get; set; } = new List<EntitySub>{ new EntitySub() { P1 = 1, P2=1.111, P3 = "第一个" }, new EntitySub() { P1 = 2,P2=2.222, P3 = "第二个" }, new EntitySub() { P1 = 3,P2=3.333, P3 = "第三个" } }; public Dictionary<int, string> P27 { get; set; } = new Dictionary<int, string>() { { 1,"aaa"}, { 2,"bbb"}, { 3,"ccc"}, }; public Dictionary<string, int> P28 { get; set; } = new Dictionary<string, int>() { { "aaa",1}, { "bbb",2}, { "ccc",3}, }; } }View Code
子模型:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KLV { internal class EntitySub { public int P1 { get; set; } public double P2 { get; set; } public string P3 { get; set; } } }View Code
自定义异常:
using System; namespace KLV { /// <summary> /// 自定义一个Ktv异常类,便于识别异常类型 /// </summary> public class KLVException : Exception { public KLVException(string errMessage) : base(errMessage) { } } }View Code
用到的基础类型转字节数组的扩展方法:
using System; using System.Collections.Generic; using System.Net; namespace KLV { public static class BitConverterExtend { /// <summary> /// 得到short的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this short num, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); //大端网络字节序 } return BitConverter.GetBytes(num); } /// <summary> /// 得到ushort的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this ushort num, bool bigEndian = true) { return ((short)num).GetBytes(bigEndian); //这样比反转数组快 } /// <summary> /// 得到int的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this int num, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); } return BitConverter.GetBytes(num); } /// <summary> /// 得到uint的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this uint num, bool bigEndian = true) { return ((int)num).GetBytes(bigEndian); } /// <summary> /// 得到long的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this long num, bool bigEndian = true) { if (bigEndian) { num = IPAddress.HostToNetworkOrder(num); } return BitConverter.GetBytes(num); } /// <summary> /// 得到ulong的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this ulong num, bool bigEndian = true) { return ((long)num).GetBytes(bigEndian); } /// <summary> /// 得到float的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this float num, bool bigEndian = true) { byte[] arr = BitConverter.GetBytes(num); //本地是小端 if (bigEndian) { arr.ReverseOneself(); //反转为大端 } return arr; } /// <summary> /// 得到double的字节数组 /// </summary> /// <param name="num"></param> /// <param name="bigEndian">是否为大端字节序,默认大端</param> /// <returns></returns> public static byte[] GetBytes(this double num, bool bigEndian = true) { byte[] arr = BitConverter.GetBytes(num); if (bigEndian) { arr.ReverseOneself(); } return arr; } /// <summary> /// 从字节数组总的开始索引开始构建一个short数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static short ToInt16(this byte[] bs, int startIndex, bool bigEndian = true) { short num = BitConverter.ToInt16(bs, startIndex); //本地是小端 if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); //转为大端 } return num; } /// <summary> /// 从字节数组总的开始索引开始构建一个ushort数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static ushort ToUInt16(this byte[] bs, int startIndex, bool bigEndian = true) { return (ushort)bs.ToInt16(startIndex, bigEndian); //因为没有IPAddress.NetworkToHostOrder,所以直接调用ToInt16转换 } /// <summary> /// 从字节数组总的开始索引开始构建一个int数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static int ToInt32(this byte[] bs, int startIndex, bool bigEndian = true) { int num = BitConverter.ToInt32(bs, startIndex); if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); } return num; } /// <summary> /// 从字节数组总的开始索引开始构建一个uint数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static uint ToUInt32(this byte[] bs, int startIndex, bool bigEndian = true) { return (uint)bs.ToInt32(startIndex, bigEndian); } /// <summary> /// 从字节数组总的开始索引开始构建一个long数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static long ToInt64(this byte[] bs, int startIndex, bool bigEndian = true) { long num = BitConverter.ToInt64(bs, startIndex); if (bigEndian) { num = IPAddress.NetworkToHostOrder(num); } return num; } /// <summary> /// 从字节数组总的开始索引开始构建一个ulong数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static ulong ToUInt64(this byte[] bs, int startIndex, bool bigEndian = true) { return (ulong)(bs.ToInt64(startIndex, bigEndian)); } /// <summary> /// 从字节数组总的开始索引开始构建一个float数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static float ToFloat(this byte[] bs, int startIndex, bool bigEndian = true) { if (bigEndian) { return BitConverter.ToSingle(bs.ReverseCopy(startIndex, 4), 0); } return BitConverter.ToSingle(bs, startIndex); } /// <summary> /// 从字节数组总的开始索引开始构建一个double数值 /// </summary> /// <param name="bs">外部传入的字节数组,内部不能更改其数据</param> /// <param name="startIndex">开始索引</param> /// <param name="bigEndian">是否是大端字节序,默认真</param> /// <returns></returns> public static double ToDouble(this byte[] bs, int startIndex, bool bigEndian = true) { if (bigEndian) { return BitConverter.ToDouble(bs.ReverseCopy(startIndex, 8), 0); } return BitConverter.ToDouble(bs, startIndex); } /// <summary> /// 反转传入的数组的数据 /// </summary> public static void ReverseOneself(this IList<byte> bytes) { int len = bytes.Count / 2; for (int i = 0; i < len; i++) { (bytes[bytes.Count - 1 - i], bytes[i]) = (bytes[i], bytes[bytes.Count - 1 - i]); } } /// <summary> /// 从bytes中反转拷贝数据,不破坏原bytes的数据 /// </summary> public static byte[] ReverseCopy(this IList<byte> bytes, int startIndex, int length) { byte[] arr = new byte[length]; for (int i = 0; i < length; i++) { arr[i] = bytes[startIndex + length - 1 - i]; } return arr; } } }View Code
标签:改进版,C#,lock,int,key,Offset,byte,序列化,public From: https://www.cnblogs.com/luludongxu/p/17303934.html