不同.NET版本下BinaryFormatter序列化同一对象结果不一致问题
字节数组不一致的核心原因是你使用了BinaryFormatter做序列化,这个类从设计层面就不支持跨运行时版本、跨进程的序列化兼容,且微软早已将其标记为存在严重安全风险的废弃组件,明确禁止在网络传输等跨边界场景使用。
你当前服务端运行在.NET 5,客户端运行在.NET 6,两个版本的核心运行时程序集版本、类型元数据生成规则、BinaryFormatter内部序列化逻辑均存在差异:BinaryFormatter序列化时会将类型所属的完整程序集信息、运行时相关的元数据全部写入字节流,即便你自定义的Message类代码完全一致,两端生成的序列化结果也会因为框架版本差异出现长度、内容不一致的问题,自然无法跨端反序列化。
立即弃用BinaryFormatter,替换为跨版本、跨平台兼容的标准化序列化格式,以下是两种适用于IM场景的常用方案:
方案1:使用内置System.Text.Json(零额外依赖,实现最简单)
该方案是.NET生态下跨端传输的首选,无需引入第三方依赖,只要两端统一序列化规则即可保证结果一致,后续甚至支持非.NET客户端对接。
- 移除
Message类上的[Serializable]特性,该特性仅为BinaryFormatter等老旧序列化器设计,JSON序列化不需要 - 将序列化/反序列化逻辑替换为以下实现:
// 对象转字节数组 public static byte[] ObjectToBytes<T>(T obj) { return JsonSerializer.SerializeToUtf8Bytes(obj, new JsonSerializerOptions { // 两端保持相同的配置规则即可 PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); } // 字节数组转对象 public static T BytesToObject<T>(byte[] data) { return JsonSerializer.Deserialize<T>(data, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); }
方案2:使用Protobuf二进制序列化(高性能、小体积,适合高频消息场景)
如果你的IM应用消息传输频率高,对序列化性能、消息包体积有更高要求,可以选择Protobuf格式,它是跨语言兼容的标准化二进制序列化协议,序列化速度比JSON快数倍,生成的消息体积也更小。
你可以选择官方Google.Protobuf库,或者使用更适配现有C#类写法的protobuf-net库,按照库文档给类添加对应序列化标记后,即可实现跨版本一致的序列化/反序列化。
重要提示:不要尝试通过修改
BinaryFormatter绑定配置、强行对齐两端程序集版本的方式临时修复问题,这种方式不仅维护成本极高,后续任意一端升级.NET版本就会再次失效,还会面临BinaryFormatter自带的远程代码执行安全漏洞风险。
内容的提问来源于stack exchange,提问作者Mamink

