解决

Netty 框架自带了 JsonObjectDecoderJSON 对象解码器,它属于在四种常见的解码器之外的解码器。除此之外,还有 base64 解码器、xml 解码器等。可以在 netty-codec 包下找到所有 netty 支持的解码器。

pipeline.addLast(new JsonObjectDecoder(4096))
    .addLast(new StringDecoder())
    .addLast(new StringEncoder())
    .addLast(new HDTcpSocketHandler());

其中 4096 代表 1024 * 4,使用 UTF-8 编码,则一个字符占用四个字节,即限定最多解析 1024 个字符长度的 JSON 字符串。

HDTcpSocketHandler 是自定义的消息处理器,其他两个字符串编码器、解码器属于正常处理,特别加的 JsonObjectDecoder 是为了解决沾包、半包(拆包)问题的。

netty系列之:netty中的核心解码器 json

它的本质是通过判断 json 对象中的分割符来分割多个 json 字符串,然后将分割后的 json 字符串存入 ByteBuf 中输出。

Netty 处理消息主要步骤

1. 首先是启动 NettyServer

官方文档 4.x 用户指南 里使用 main 方法去启动,而在 Spring 中则直接注册为 @Component,使用另开一个线程去初始化,主要是为了让主进程继续加载其他的服务。

    @PostConstruct
    public void init() {
        log.debug("Netty Server init");
//        initNetty();
        new Thread(this::initNetty).start();

    }

初始化中 childHandler 设置 ChannelInitializer 通道初始化。

2. ChannelInitializer 通道初始化

官网的 demo 是直接绑定消息处理器,但这里需要额外空闲状态读写超时时间和超时处理(关闭通道和会话)。

pipeline.addLast("counter", counterHandler)
    .addLast("myDispatch", new ProtocolDecoder())
    .addLast(new IdleStateHandler(150, 150, 150))
    .addLast(new HeartbeatHandler());

3. 按照不同的通讯协议分别添加响应的编码器和解码器

目前接入了 5 个厂家,每个厂家都有自己的通讯协议,而每个通讯协议都有自己的特别的编码格式。有根据字节码头几位判断的,也有使用 mqtt 协议的,还有结尾为换行符或者特殊字符的。

这里分别使用了 LengthFieldBasedFrameDecoder(基于长度的域解码器)、LineBasedFrameDecoder 行帧解码器、DelimiterBasedFrameDecoder 固定分隔符解码器,还有一种 FixedLengthFrameDecoder 固定长度编码器暂时未使用到。这四种解码器就是常见的可以解决沾包、半包问题的四种类型解码器。

其他的,基于特殊格式的解码器,有 JsonObjectDecoder JSON 对象解码器,XmlFrameDecoder xml 解码器等。

当然,特别的还有自己通讯协议的,比如 WebSocketHttpMqtt等。这些特殊协议有自己的框架边界,所以不会出现沾包、半包的问题。

后记

本来一开始没有找到这个 JsonObjectDecoder JSON 对象解码器,是准备找找教程,自定义一个解码器来识别 JSON 格式编码的,看了 JSON 对象解码器才感觉到算法的复杂性。

另外,几种解码器都有一个 maxLength 最大长度,超过最大长度就放弃识别,表示识别失败,这也是非常必要的,不然如果出现非预期的报文,会一直在那边等待结束符或者整体匹配格式,可能导致后续正常的报文丢失和通道的拥堵。

参考:
什么是粘包和半包?怎么解决?
Netty入门与实战
Netty(3)进阶篇|半包粘包、编解码器