资料
netty处理器链初始化源码分析netty处理器链读数据执行流程源码分析LineBasedFrameDecoder解决粘包半包源码分析netty粘包半包问题常见解决方案及总结netty处理器链架构图及其总结
解决粘包半包主要目的
解决粘包半包问题其实就是围绕一个主题,那就是从TCP包中,通过一定字节作为标记,然后标记前字节必定是一个可反序列化的字节Buff, 这样就可以解决粘包半包问题
解决粘包方案
1.通过指定固定字节数作为分隔标准
2.指定特殊字符作为分隔标准
3.默认使用换行符作为分隔标准(指定特殊字符作为分隔标准一种情况)
解决粘包方案图解
FixedLengthFrameDecoder
//截取固定长度的字节作为一个包
//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取FixedLength应该截取的字节长度
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
//截取定义长度的frameLength的Buff
//1.如果长度不够,那么不截取,直接返回null,不执行下去(半包)
//2.如果长度大于等于frameLength长度,截取frameLength长度字节的Buff(粘包)
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (in.readableBytes() < frameLength) {
return null;
} else {
return in.readRetainedSlice(frameLength);
}
}
DelimiterBasedFrameDecoder
//根据指定的特殊字符分割作为一个包
//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取指定特殊字符前面的字节长度
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
int minFrameLength = Integer.MAX_VALUE;
ByteBuf minDelim = null;
for (ByteBuf delim: delimiters) {
//找到特殊字符的字节内容长度,根据delim去截取
int frameLength = indexOf(buffer, delim);
if (frameLength >= 0 && frameLength < minFrameLength) {
//minFrameLength表示这个包的长度
minFrameLength = frameLength;
//
minDelim = delim;
}
}
//minFrameLength是包长度
//minDelimLength是指定特殊字符的长度
//每次一帧长度为minFrameLength+minDelimLength
if (minDelim != null) {
if (stripDelimiter) {
frame = buffer.readRetainedSlice(minFrameLength);
buffer.skipBytes(minDelimLength);
} else {
frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
}
return frame;
}
}
LineBasedFrameDecoder
//根据指定的特殊字符分割作为一个包
//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取换行符前的字节长度
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
//寻找到换行符然后截取子Buff
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
//找到换行符的位置eol
final int eol = findEndOfLine(buffer);
if (!discarding) {
if (eol >= 0) {
final ByteBuf frame;
//eol是换行符前面字节长度
//delimLength是换行符长度
//截取的总长度是eol+delimLength
final int length = eol - buffer.readerIndex();
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
if (stripDelimiter) {
//获取完整包字节
frame = buffer.readRetainedSlice(length);
//跳过换行符长度字节数
buffer.skipBytes(delimLength);
} else {
frame = buffer.readRetainedSlice(length + delimLength);
}
return frame;
}
}