第 10 章 从行协议走向自定义协议
10.1 先写一份人能读懂的协议说明
在写编解码器前,定义字段与约束。例如:
| 字段 | 大小 | 约定 |
|---|---|---|
| magic | 2 字节 | 固定值,帮助拒绝明显错误输入 |
| version | 1 字节 | 协议版本 |
| type | 1 字节 | 请求、应答、心跳等 |
| requestId | 8 字节 | 关联请求与应答 |
| bodyLength | 4 字节 | 正文字节数,大端序 |
| body | 可变 | UTF-8 JSON 或约定的二进制格式 |
以上是设计练习,主线工程没有实现这个二进制协议。不要把这张表误认为可以直接与行协议客户端通信。
10.2 从字段表推出帧参数
头部总长为 16 字节,bodyLength 位于偏移 12,宽度 4,表示正文长度。若希望保留整个头部供后续解码器读取,可以使用:
new LengthFieldBasedFrameDecoder(1048576, 12, 4, 0, 0)长度字段末端正好是头部末端,因此调整值为 0;最后一个 0 表示不剥离头部。最大整帧为 1 MiB,正文最多为 1 MiB 减 16 字节。
之后的业务消息解码器应校验 magic、版本、type 和长度,再解析正文。字段值不可信,不能因为它来自已建立的 TCP 连接就跳过校验。
10.3 请求应答关系
客户端为请求分配 requestId,保存待完成结果;服务端应答携带相同编号。应答到达后找到对应请求并完成它;超时后删除记录,避免内存一直增长。
请求编号必须在尚未完成的请求范围内保持唯一。断线时要统一失败化尚未完成的请求,否则调用方可能永远等待。若发生重连,还要考虑旧连接的应答与新连接请求之间的隔离。
10.4 序列化与安全边界
JSON 便于初学和调试,但仍须限制正文大小、嵌套深度和字段范围。不要为了省事,把不可信网络输入交给 Java 原生对象反序列化。
公网传输敏感信息时,应使用 TLS,并在客户端验证证书和目标主机身份。加密、认证、授权是不同的问题:建立加密连接,不等于请求者有操作数据的权限。
小练习: 为什么只有 requestId,还不足以保证断线重试不会重复扣款?