C#客户端反序列化服务器断开响应时触发SerializationException异常
问题根因
- 同个Socket的接收操作被两处逻辑争抢:客户端存在独立的后台接收线程,会持续读取Socket数据并调用
ByteArrayToObject做反序列化,而DisconnectServer方法里又单独调用了一次Receive读取服务端的断开响应。服务端返回的断开消息字符串字节流,有时被DisconnectServer的Receive抢到就能正常执行,有时被后台接收线程抢到,就会把普通字符串的编码字节传入反序列化方法,触发SerializationException。 - 缺少统一的通信协议规范:当前的TCP通信没有约定报文边界和报文类型,接收端无法区分收到的内容是需要反序列化的业务对象,还是控制类的字符串消息,也无法保证每次都读取到完整的单条报文。
修复方案
- 统一接收入口:移除业务方法里直接调用
Receive的逻辑,所有Socket数据接收都走统一的后台线程,避免多线程抢读导致的数据流错乱。 - 新增报文协议头:所有发送的报文都增加固定长度的头部,至少包含两个字段:
- 报文总长度(4字节整数):接收端先读4字节拿到长度,再按长度读取完整的报文内容,避免粘包/半包问题
- 报文类型标识(1字节即可):标记当前报文是需要反序列化的业务对象,还是普通控制字符串,接收端根据类型选择对应的处理逻辑,不会把字符串丢给反序列化方法
- 调整断开流程时序:客户端发起断开请求后,先暂停后台接收线程的反序列化逻辑,等拿到服务端返回的断开响应、执行完Socket的
Shutdown和Close操作后,再销毁后台接收线程。 - 可选优化:
BinaryFormatter存在严重的安全漏洞,微软官方已标记为不安全类型并禁止在生产环境使用,后续可以替换为Protobuf、System.Text.Json等更安全的序列化方案。 - 服务端兼容优化:发送断开响应后可以增加短时间延迟再执行
Shutdown和Close,或者等待客户端先发起断开,避免FIN包提前到达导致客户端接收不全。
内容的提问来源于stack exchange,提问作者Alexander Khurnov
相关产品推荐
相关产品推荐

