c# - 手动打包字节以在网络上发送
问题描述
我有一个具有以下变量的对象:
bool firstBool;
float firstFloat; (0.0 to 1.0)
float secondFloat (0.0 to 1.0)
int firstInt; (0 to 10,000)
我正在使用 ToString 方法来获取可以通过网络发送的字符串。扩大规模我遇到了这个占用的数据量的问题。字符串现在看起来像这样:
"false:1.0:1.0:10000"
这是 19 个字符,每 2 个字节,所以 38 个字节
我知道我可以通过手动将数据存储在 4 个字节中来节省这个大小,如下所示:
A|B|B|B|B|B|B|B
C|C|C|C|C|C|C|D
D|D|D|D|D|D|D|D
D|D|D|D|D|X|X|X
A = bool(0 or 1), B = int(0 to 128), C = int(0 to 128), D = int(0 to 16384), X = Leftover bits
- 我将其转换为
float(0.0 to 1.0)
,int(0 to 128)
因为我可以在另一端重建它们,而且准确性并不是非常重要。
我一直在尝试使用 BitArray 和 byte[] 将数据转换为二进制结构。
经过一些实验,我最终完成了这个序列化过程(我知道它需要清理和优化)
public byte[] Serialize() {
byte[] firstFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(firstFloat * 128)); //Convert the float to int from (0 to 128)
byte[] secondFloatBytes = BitConverter.GetBytes(Mathf.FloorToInt(secondFloat * 128)); //Convert the float to int from (0 to 128)
byte[] firstIntData = BitConverter.GetBytes(Mathf.FloorToInt(firstInt)); // Get the bytes for the int
BitArray data = new BitArray(32); // create the size 32 bitarray to hold all the data
int i = 0; // create the index value
data[i] = firstBool; // set the 0 bit
BitArray ffBits = new BitArray(firstFloatBytes);
for(i = 1; i < 8; i++) {
data[i] = ffBits[i-1]; // Set bits 1 to 7
}
BitArray sfBits = new BitArray(secondFloatBytes);
for(i = 8; i < 15; i++) {
data[i] = sfBits[i-8]; // Set bits 8 to 14
}
BitArray fiBits = new BitArray(firstIntData);
for(i = 15; i < 29; i++) {
data[i] = fiBits[i-15]; // Set bits 15 to 28
}
byte[] output = new byte[4]; // create a byte[] to hold the output
data.CopyTo(output,0); // Copy the bits to the byte[]
return output;
}
从这种结构中取出信息比把它放入这种形式要复杂得多。我想我可能可以使用按位运算符和位掩码来锻炼一些东西。
事实证明这比我预期的要复杂。我认为访问 byte[] 的位以直接操作数据、提取位范围、然后转换回重建对象所需的值会非常容易。这种类型的数据序列化是否有最佳实践?有谁知道我可以阅读的教程或示例参考?
解决方案
标准且高效的序列化方法有:
使用
BinaryWriter
/BinaryReader
:public byte[] Serialize() { using(var s = new MemoryStream()) using(var w = new BinaryWriter(s)) { w.Write(firstBool); w.Write(firstFloat); ... return s.ToArray(); } } public void Deserialize(byte[] bytes) { using(var s = new MemoryStream(bytes)) using(var r = new BinaryReader(s)) { firstBool = r.ReadBool(); firstFload = r.ReadFloat(); ... } }
BinaryWriter
/BinaryReader
快得多(大约 7 倍)。Protobuf 更灵活、更易于使用、非常流行,并且序列化的字节数减少了大约 33%。(当然,这些数字是数量级的,取决于您序列化的内容和方式)。
现在基本上BinaryWriter
会写 1 + 4 + 4 + 4 = 13 个字节。您可以通过将值转换为 bool、byte、byte、short 来将其缩小到 5 个字节,然后按照您想要的方式对其进行舍入。最后,如果您真的想要,很容易将 bool 与您的字节之一合并以获得 4 个字节。
我并不反对手动序列化。但就性能而言,它必须物有所值。代码非常不可读。直接在字节上使用位掩码和二进制移位,但要尽可能简单。不要使用位数组。它很慢而且不可读。
推荐阅读
- javascript - 制表符:cellEdited 后会发生什么回调?
- c# - C# 应用程序中的 IronPython - SyntaxErrorException: 'invalid syntax'
- r - 错误代码“'closure'类型的对象不是子集”的问题
- javascript - 在 react datepicker 中设置和获取 localStorage 中的日期和时间
- c# - PHP 上的 C# ECKey 模拟
- java - 模块化 FXML 控制器 JavaFX 上的 hostServices
- css - 基于英寸而不是像素的 CSS @Media 查询
- python - 如何在字典中创建范围
- javascript - react.js 如何实现多输入搜索?
- python - pandas 根据组 min max 动态交叉加入