TCP 以流的方式进行数据传输, 上层的应用协议为了对消息进行区分, 往往采用如下 4 中方式.
消息长度固定, 累计读取到长度总和为定长 LEN 的报文后, 就认为读到了一个完整的消息; 将计数器置位, 重新开始读取下一个数据报;
将回车换行符作为消息结束符, 例如 FTP 协议, 这种方式在文本协议中应用比较广泛.
将特殊的分隔符最为消息的结束标志, 回车换行符就是一种特殊的结束分隔符;
通过在消息头中定义长度字段来标识消息的总长度.
Netty 对上面 4 种应用做了统一的抽象, 提供了 4 种解码器来解决对应用的问题.
之前我写了 LineBasedFrameDecoder 解码器的使用, 今天开始学习如何使用 DelimiterBasedFrameDecoder 和 FixedLengthFrameDecoder.
DelimiterBasedFrameDecoder 应用开发
通过对 DelimiterBasedFrameDecoder 的使用, 我们可以自动完成以分隔符作为码流结束标识的消息的解码.
还是修改 initChannel 方法
ByteBuf delimiter = Unpooled.copiedBuffer(“$_”.getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeServerHandler());
我们使用 $_ 作为分隔符, 创建 DelimiterBasedFrameDecoder 对象, 将其加入到 ChannelPipeline 中. DelimiterBasedFrameDecoder 有多个构造方法, 这里我们传递两个参数: 第一个 1024 表示单条消息的最大长度, 当达到该长度后仍然没有检查到分隔符, 将抛出 TooLongFrameException 异常, 防止由于异常码流缺失分隔符导致的内存溢出, 这是 Netty 解码器的可靠性保护; 第二个参数就是分隔符缓冲对象.
由于我们设置 DelimiterBasedFrameDecoder 过滤掉了分隔符, 所以返回给客户端时需要在请求消息尾部拼接分隔符 $_.
FixedLengthFrameDecoder 应用开发
FixedLengthFrameDecoder 是固定长度解码器, 它能够按照指定的长度对消息进行自动解码.
ch.pipeline().addLast(new FixedLengthFrameDecoder(10));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeServerHandler());
利用 FixedLengthFrameDecoder 解码器, 无论一次接收到多少数据报, 他都会按照构造函数中设置的固定长度进行解码, 如果是半包消息, FixedLengthFrameDecoder 会缓存半包消息并等待下个包到达后进行拼包, 直到读取到一个完整的包.