WebSocketStompClient发送Protobuf字节数组报错求助
我之前踩过Stomp结合Protobuf发送二进制消息的坑,结合你的描述——只有填写int64/double类型的id字段才触发StompConversionException: Frame must be terminated...,空id就正常,这大概率是Protobuf序列化字节和Stomp协议的控制字符冲突导致的,给你几个具体的排查方向:
先查序列化字节里有没有Stomp的终止符
Stomp协议的帧结束标记是十六进制的00(空字节),而Protobuf对int64的varint编码,当id值为0或者某些小数值时,会直接生成00字节。Stomp客户端看到这个字节会误以为帧已经结束,提前截断消息,导致后续字节缺失,抛出终止异常。
你可以把PushHeader.toByteArray()的结果转成十六进制打印出来,重点看id字段对应的字节段里有没有00。检查消息转换器的配置是否正确
Spring的WebSocketStompClient默认可能会用文本类的转换器处理消息,这会把二进制字节当成UTF-8文本解码再编码,破坏Protobuf的原始数据。
一定要确保优先配置ByteArrayMessageConverter,避免其他转换器干扰:WebSocketStompClient stompClient = new WebSocketStompClient(new SockJsClient(...)); // 把字节数组转换器放在最前面 stompClient.setMessageConverter(new ByteArrayMessageConverter());同时检查有没有其他转换器(比如Jackson的JSON转换器)被添加到客户端中,它们可能会优先处理消息导致二进制数据异常。
修改Protobuf字段的编码类型
Protobuf的int64默认用varint编码,这种编码容易产生单字节的00。你可以把id字段改成固定长度的编码,比如fixed64或sfixed64,这样序列化后的字节不会出现单字节的终止符:message PushHeader { fixed64 id = 1; // 替换原来的int64 id = 1; // 其他字段保持不变 }对于double类型,它的Protobuf编码是固定8字节的IEEE 754格式,虽然很少直接产生
00,但如果你的数值刚好对应包含这个字节的二进制,也可以尝试用类似思路验证。临时验证:Base64编码后发送
你可以先把Protobuf序列化的字节数组转成Base64字符串,发送字符串类型的Stomp消息,服务端再解码成字节数组解析Protobuf。如果这样能正常发送,就坐实了是二进制字节中的特殊字符和Stomp协议冲突的问题。不过这只是验证手段,Base64会增加带宽开销,不建议作为最终方案。升级Spring依赖版本
某些旧版本的Spring WebSocket Stomp客户端在处理包含空字节的二进制消息时存在bug。尝试把Spring Boot或Spring WebSocket的依赖升级到最新的稳定版本(比如2.7.x或3.x系列),看看问题是否自动解决。
内容的提问来源于stack exchange,提问作者David Ramsaran

