TCP以流的方式进行数据传输,上层的应用协议为了对消息进行区分,往往采用如下四种方式:
-
消息长度固定,累计读取到长度总和为定长LEN的报文后,就认为读取到了一个完整的消息;将计数器置位,重新开始读取下一个数据报;
-
将回车换行符作为消息结束符,例如FTP协议,这种方式在文本协议中应用比较广泛;
-
将特殊的分隔符作为消息的结束标志,回车换行符就是一种特殊的结束分隔符;
-
通过在消息头中定义长度字段来标识消息的总长度。
Netty对上面四种应用做了统一的抽象,提供了四种解码器来解决对应的问题,使用起来非常方便。使用这些解码器,用户不需要自己对读取的报文进行人工解码,也不需要考虑TCP的粘包与拆包。
Netty提供解码器如下: -
DelimiterBasedFrameDecoder 基于消息边界方式进行粘包拆包处理
-
FixedLengthFrameDecoder 基于固定长度消息进行粘包拆包处理
-
LengthFieldBasedFrameDecoder 基于消息头指定消息长度进行粘包拆包处理
-
LineBasedFrameDecoder 基于行来进行消息粘包拆包处理
1.DelimiterBasedFrameDecoder原理:
DelimiterBasedFrameDecoder类属性
/**
* 分隔符
*/
private final ByteBuf[] delimiters;
/**
* 最大帧长度
*/
private final int maxFrameLength;
/**
* 是否跳过分隔符,就是最终解码的数据里面是否包含分隔符
*/
private final boolean stripDelimiter;
/**
* 为true说明发现督导的数据已经超过maxFrameLength,立即报TooLongFrameException,
* false-读完整个帧数据后再报
*/
private final boolean failFast;
/**
* 当前的解码器是否处于discardingTooLongFrame状态,是一个标志位,在构造函数里不能进行设置,
* 只能在解码器里进行设置
*/
private boolean discardingTooLongFrame;
/**
* 是一个状态属性,就是说出现了超长帧了,哪这个帧的长度到底是多少,就是这个长度,
* 一般来说是在发现当前buffer的可读数据超过最大帧时候进行设置
*/
private int tooLongFrameLength;
/** Set only when decoding with "\n" and "\r\n" as the delimiter. */
private final LineBasedFrameDecoder lineBasedDecoder;
主要构造函数,主要是在传递多个delimiter的时候进行slice操作,最终调用decode方法进行解码
public DelimiterBasedFrameDecoder(
int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
validateMaxFrameLength(maxFrameLength);
if (delimiters == null) {
throw new NullPointerException("delimiters");
}
if (delimiters.length == 0) {
throw new IllegalArgumentException("empty delimiters");
}
if (isLineBased(delimiters) && !isSubclass()) {
lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
this.delimiters = null;
} else {
this.delimiters = new ByteBuf[delimiters.length];
for (int i = 0; i < delimiters.length; i ++) {
ByteBuf d = delimiters[i];
validateDelimiter(d);
this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
}
lineBasedDecoder = null;
}
this.maxFrameLength = maxFrameLength;
this.stripDelimiter = stripDelimiter;
this.failFast = failFast;
}
DelimiterBasedFrameDecoder类的decode方法:
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
if (lineBasedDecoder != null) {
return lineBasedDecoder.decode(ctx, buffer);
}
// Try all delimiters and choose the delimiter which yields the shortest frame.
int minFrameLength = Integer.</