接上篇 Netty服务端源码阅读笔记(六)服务端拆包粘包和解决方法代码示例
代码不贴了,看上篇文章吧
客户端handler依次为LengthFieldPrepender用来为消息增加元数据头的handler和自定义的handler
服务端handler依次为LengthFieldBasedFrameDecoder、StringDecoder、和自定义的handler
目录
2、LengthFieldBasedFrameDecoder
2.1.1、cumulator.cumulate(ctx.alloc(), cumulation, data);
1、LengthFieldPrepender
1.1、MessageToMessageEncoder<T>
LengthFieldPrepender继承MessageToMessageEncoder<ByteBuf>
熟悉的StringEncoder也继承这个类,不过泛型不一样,是MessageToMessageEncoder<CharSequence>,这个泛型标识了本handler只支持这个类型的消息处理,如果来的消息不是这个类型,会跳过本handler
public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> {
}
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {
private final TypeParameterMatcher matcher;
// new的时候初始化matcher,获取泛型类型class
// find方法先忽略
protected MessageToMessageEncoder() {
matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I");
}
// 校验来的消息是不是泛型类型,内部就是判断泛型class.isInstance(msg)
public boolean acceptOutboundMessage(Object msg) throws Exception {
return matcher.match(msg);
}
// 出栈处理器自然得实现write方法
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// 放置转换后的新对象的数据结构,新东西哈,再研究
CodecOutputList out = null;
try {
// 判断是不是本handler需要处理得消息
if (acceptOutboundMessage(msg)) {
out = CodecOutputList.newInstance();
@SuppressWarnings("unchecked")
I cast = (I) msg;
try {
// 抽象方法,子类实现
// 把原先的消息对象(cast)转换,把新对象放到out中
encode(ctx, cast, out);
} finally {
// 释放原cast空间
// ReferenceCount引用计数,只有引用计数为0后才会释放空间
ReferenceCountUtil.release(cast);
}
if (out.isEmpty()) {
out.recycle();
out = null;
throw new EncoderException(
StringUtil.simpleClassName(this) + " must produce at least one message.");
}
} else {
// 跳过本handler,开始调用链中上一个handler的write
ctx.write(msg, promise);
}
} catch (EncoderException e) {
throw e;
} catch (Throwable t) {
throw new EncoderException(t);
} finally {
// 如果转换成功并放到了out中
if (out != null) {
final int sizeMinusOne = out.size() - 1;
if (sizeMinusOne == 0) {
//如果消息被转换后也是一条,直接获取并继续调用链handler
ctx.write(out.getUnsafe(0), promise);
} else if (sizeMinusOne > 0) {
// 如果消息被转换后成了多条,循环每条再调用链handler继续处理
if (promise == ctx.voidPromise()) {
writeVoidPromise(ctx, out);
} else {
// 如果ctx的promise对象不是write入参的promise,额外调用一个finish方法更新promise状态,为什么有这种情况还不知道
writePromiseCombiner(ctx, out, promise);
}
}
out.recycle();
}
}
}
private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) {
final ChannelPromise voidPromise = ctx.voidPromise();
for (int i = 0; i < out.size(); i++) {
ctx.write(out.getUnsafe(i), voidPromise);
}
}
private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {
final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
for (int i = 0; i < out.size(); i++) {
combiner.add(ctx.write(out.getUnsafe(i)));
}
combiner.finish(promise);
}
}
看过了父类,知道了这个LengthFieldPrepender只接受ByteBuf类型的消息,这也是我们客户端handler没有StringEncoder的原因,因为自定义的handler直接write的就是ByteBuf类型
那就剩看下这个类初始化和encode方法了
1.2、new LengthFieldPrepender
public LengthFieldPrepender(
ByteOrder byteOrder, int lengthFieldLength,
int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
// 构造函数中传入的长度段大小必须为1,2,3,4,8中的一个,表示长度段占几个字节位置
if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
lengthFieldLength != 3 && lengthFieldLength != 4 &&
lengthFieldLength != 8) {
throw new IllegalArgumentException(
"lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
lengthFieldLength);
}
ObjectUtil.checkNotNull(byteOrder, "byteOrder");
// 字节序,默认大端序
this.byteOrder = byteOrder;
// 长度段的字节长度
this.lengthFieldLength = lengthFieldLength;
// 算消息总长度时是否把长度段那几个字节算进来,默认不算,false
this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
// 调整长度,看代码应该是 算消息总长的时候的保留长度,默认0
this.lengthAdjustment = lengthAdjustment;
}
1.2.1、ByteOrder 字节序
分大端序ByteOrder.BIG_ENDIAN 和小端序ByteOrder.LITTLE_ENDIAN,在读或写入多字节值的时候使用,java默认为大端序,用代码来解释:
public static void main(String[] args) {
System.out.println("5003二进制表示::" + Integer.toBinaryString(5003));
ByteBuffer buf = ByteBuffer.allocate(10);
System.out.println("当前字节序:" + buf.order().toString());
buf.putInt(5003);
buf.flip();
for (int i = 0; i < buf.limit(); i++)
System.out.print((buf.get() & 0xFF) + ",");
buf.clear();
buf.order(ByteOrder.LITTLE_ENDIAN);
System.out.println();
System.out.println("当前字节序:" + buf.order().toString());
buf.putInt(5003);
buf.flip();
for (int i = 0; i < buf.limit(); i++)
System.out.print((buf.get() & 0xFF) + ",");
}
执行结果就一目了然
5003二进制表示::1001110001011
当前字节序:BIG_ENDIAN
0,0,19,139,
当前字节序:LITTLE_ENDIAN
139,19,0,0,
1.3、encode
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
// 获取msg的总消息长度
int length = msg.readableBytes() + lengthAdjustment;
if (lengthIncludesLengthFieldLength) {
// 如果还得算长度段,就把长度段加上
length += lengthFieldLength;
}
if (length < 0) {
throw new IllegalArgumentException(
"Adjusted frame length (" + length + ") is less than zero");
}
// 分配长度段空间,写入消息大小
// 长度段大小,1个字节2^8 = 256,最大表示的消息长度为256字节,其他类似
switch (lengthFieldLength) {
case 1:
if (length >= 256) {
throw new IllegalArgumentException(
"length does not fit into a byte: " + length);
}
out.add(ctx.alloc().buffer(1).order(byteOrder).writeByte((byte) length));
break;
case 2:
if (length >= 65536) {
throw new IllegalArgumentException(
"length does not fit into a short integer: " + length);
}
out.add(ctx.alloc().buffer(2).order(byteOrder).writeShort((short) length));
break;
case 3:
if (length >= 16777216) {
throw new IllegalArgumentException(
"length does not fit into a medium integer: " + length);
}
out.add(ctx.alloc().buffer(3).order(byteOrder).writeMedium(length));
break;
case 4:
out.add(ctx.alloc().buffer(4).order(byteOrder).writeInt(length));
break;
case 8:
out.add(ctx.alloc().buffer(8).order(byteOrder).writeLong(length));
break;
default:
throw new Error("should not reach here");
}
// 再把原消息体塞进去
out.add(msg.retain());
}
2、LengthFieldBasedFrameDecoder
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {}
2.1、ByteToMessageDecoder
入栈处理器,自然是要看channelRead()方法
先大致了解思想:每次channel来ByteBuf消息,ByteToMessageDecoder中会有一个ByteBuf变量存放,如果没达到处理要求,则返回null,不在handler链上执行。下次继续来消息,会把这次的和上次的消息合并起来再去处理,从而达到处理要求之后,处理完毕执行handler链
public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
ByteBuf cumulation;
private Cumulator cumulator = MERGE_CUMULATOR;
private byte decodeState = STATE_INIT; // 当前处理状态,右下边三种状态
private static final byte STATE_INIT = 0;// 初始化
private static final byte STATE_CALLING_CHILD_DECODE = 1;// 正在调用子类decode方法
private static final byte STATE_HANDLER_REMOVED_PENDING = 2;// 处理器即将被移除
private int discardAfterReads = 16;
private int numReads;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 只处理ByteBuf类型的消息
if (msg instanceof ByteBuf) {
// 保存处理结果的集合list
CodecOutputList out = CodecOutputList.newInstance();
try {
ByteBuf data = (ByteBuf) msg;
// cumulation 是一个缓存的byteBuf
// 如果本次还没读到指定长度暂时不会处理这个消息,本次读到的数据就放在这个byteBuf里边
// 下次读的时候两次的会合并起来再去处理
first = cumulation == null;
if (first) {
cumulation = data;
} else {
// cumulator.cumulate合并两次的byteBuf
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
}
// 调用子类decode方法
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
throw e;
} catch (Exception e) {
throw new DecoderException(e);
} finally {
// 如果cumulation不为空,且不可读,释放空间
if (cumulation != null && !cumulation.isReadable()) {
numReads = 0;
cumulation.release();
cumulation = null;
} else if (++ numReads >= discardAfterReads) {
// 超过最大读取次数16次默认,同AbstractNioByteChannel.NioByteUnsafe#read中每次do while循环最多16次
numReads = 0;
discardSomeReadBytes();
}
// 调用其他channelRead
int size = out.size();
decodeWasNull = !out.insertSinceRecycled();
fireChannelRead(ctx, out, size);
out.recycle();
}
} else {
// 非ByteBuf类的消息直接跳过
ctx.fireChannelRead(msg);
}
}
static void fireChannelRead(ChannelHandlerContext ctx, List<Object> msgs, int numElements) {
if (msgs instanceof CodecOutputList) {
fireChannelRead(ctx, (CodecOutputList) msgs, numElements);
} else {
for (int i = 0; i < numElements; i++) {
ctx.fireChannelRead(msgs.get(i));
}
}
}
/**
* Get {@code numElements} out of the {@link CodecOutputList} and forward these through the pipeline.
*/
static void fireChannelRead(ChannelHandlerContext ctx, CodecOutputList msgs, int numElements) {
for (int i = 0; i < numElements; i ++) {
ctx.fireChannelRead(msgs.getUnsafe(i));
}
}
protected final void discardSomeReadBytes() {
if (cumulation != null && !first && cumulation.refCnt() == 1) {
// 把还未读取的数据挪到前边,丢弃其他部分,以避免额外内存带来的内存消耗
cumulation.discardSomeReadBytes();
}
}
}
2.1.1、cumulator.cumulate(ctx.alloc(), cumulation, data);
public static final Cumulator MERGE_CUMULATOR = new Cumulator() {
@Override
public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
final ByteBuf buffer;
if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes()
|| cumulation.refCnt() > 1 || cumulation.isReadOnly()) {
// Expand cumulation (by replace it) when either there is not more room in the buffer
// or if the refCnt is greater then 1 which may happen when the user use slice().retain() or
// duplicate().retain() or if its read-only.
// 1、写索引大于总容量-可读数 表示没有足够的空间来合并了,需要扩容
// 2、引用数大于1(调用过slice().retain()或duplicate().retain())
// 3、cumulation是只读的
buffer = expandCumulation(alloc, cumulation, in.readableBytes());
} else {
buffer = cumulation;
}
// 合并
buffer.writeBytes(in);
// 释放原byteBuf
in.release();
return buffer;
}
};
static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) {
ByteBuf oldCumulation = cumulation;
// 重新分配一个足够大的
cumulation = alloc.buffer(oldCumulation.readableBytes() + readable);
// 从原来的buf复制到新buf上
cumulation.writeBytes(oldCumulation);
oldCumulation.release();
return cumulation;
}
2.1.2、callDecode
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try {
while (in.isReadable()) {
int outSize = out.size();
// 如果已经decode完毕,out有值了,继续handler链的channelRead
if (outSize > 0) {
fireChannelRead(ctx, out, outSize);
out.clear();
if (ctx.isRemoved()) {
break;
}
outSize = 0;
}
// 可读长度
int oldInputLength = in.readableBytes();
// 调用decode方法,处理结果会被放在out这个list中
decodeRemovalReentryProtection(ctx, in, out);
// 如果手动移除处理器了,跳出循环
if (ctx.isRemoved()) {
break;
}
// outSize == 0 ,这里表示out没有解码出数据
if (outSize == out.size()) {
// 子类并没有从in中读取任何数据,表示本次还没有读取条件
// 例如没有到指定长度,暂时不处理,等下次一块处理
if (oldInputLength == in.readableBytes()) {
break;
} else {
// 读取数据了,但是没有返回值,继续读取
continue;
}
}
// 这里out.size > 0 ,且没读任何数据,自定义的decode有问题,报个错
if (oldInputLength == in.readableBytes()) {
throw new DecoderException(
StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
}
// 这个应该是特殊解码用
if (isSingleDecode()) {
break;
}
}
} catch (DecoderException e) {
throw e;
} catch (Exception cause) {
throw new DecoderException(cause);
}
}
final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
throws Exception {
// 状态改为 调用子类decode
decodeState = STATE_CALLING_CHILD_DECODE;
try {
// 子类实现,解码后的消息对象放到out中
decode(ctx, in, out);
} finally {
// 是否要移除handler
boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
decodeState = STATE_INIT;
if (removePending) {
handlerRemoved(ctx);
}
}
}
2.2 decode
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
private final ByteOrder byteOrder;
private final int maxFrameLength;
private final int lengthFieldOffset;
private final int lengthFieldLength;
private final int lengthFieldEndOffset;
private final int lengthAdjustment;
private final int initialBytesToStrip;
private final boolean failFast;
private boolean discardingTooLongFrame;
private long tooLongFrameLength;
private long bytesToDiscard;
public LengthFieldBasedFrameDecoder(
ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
this.byteOrder = byteOrder;// 字节序,默认大端序
this.maxFrameLength = maxFrameLength;// 需要解吗的段最大长度
this.lengthFieldOffset = lengthFieldOffset;// 长度标识的偏移量
this.lengthFieldLength = lengthFieldLength;// 长度标识的长度,1,2,3,4,8
this.lengthAdjustment = lengthAdjustment;// 消息总长,预留长度
lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;// 消息体开头的偏移量
this.initialBytesToStrip = initialBytesToStrip;// 不知道,默认0
this.failFast = failFast;// 默认true
}
@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);
}
}
}
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
// 首次进来false
if (discardingTooLongFrame) {
discardingTooLongFrame(in);
}
// 消息的可读字节数还没有 长度段的长度 长,过了过了
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
// 读索引 + 长度段的偏移量 才是真正的长度段偏移量
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
// 从in中 actualLengthFieldOffset 这个位置开始读lengthFieldLength这么长字节
// 按byteOrder字节序读出实际消息的long长度
long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);
// 报错!!先把in这个lengthFieldEndOffset位置前边的直接不看了跳过,然后报错
if (frameLength < 0) {
failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
}
// 算算实际消息长度加上长度段一共多长(还有个调整长度,暂时可以忽略它)
frameLength += lengthAdjustment + lengthFieldEndOffset;
// 这什么情况??加起来还没长度段长,跳过offset长度然后报错报错
if (frameLength < lengthFieldEndOffset) {
failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
}
// 总长大于设置的 可操作最大Frame长度
if (frameLength > maxFrameLength) {
// 跳过这一段frameLength 不读了skip跳过
exceededFrameLength(in, frameLength);
return null;
}
// 可读字节数没够长度,暂时不读,先在父类攒着
int frameLengthInt = (int) frameLength;
if (in.readableBytes() < frameLengthInt) {
return null;
}
// 这一步我不知道干啥的,initialBytesToStrip默认0,先忽略
if (initialBytesToStrip > frameLengthInt) {
failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip);
}
in.skipBytes(initialBytesToStrip);
// 总算攒够足够长的ByteBuf了,开始截取
int readerIndex = in.readerIndex();
int actualFrameLength = frameLengthInt - initialBytesToStrip;
ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
in.readerIndex(readerIndex + actualFrameLength);
return frame;
}
private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"negative pre-adjustment length field: " + frameLength);
}
// 太长了处理不了,送走,不处理
private void exceededFrameLength(ByteBuf in, long frameLength) {
// discard < 0 ,计算出的消息总长如果小于in中可读字节数,那就把frameLength忽略掉
// discard > 0 ,本次in还不是全部消息,下次接进来的数据还得继续忽略
long discard = frameLength - in.readableBytes();
tooLongFrameLength = frameLength;
if (discard < 0) {
//直接忽略整个frameLength
in.skipBytes((int) frameLength);
} else {
// discardingTooLongFrame 这就是下次进来需要继续忽略的标志
// bytesToDiscard 下拨消息忽略多少字节
discardingTooLongFrame = true;
bytesToDiscard = discard;
in.skipBytes(in.readableBytes());
}
// 是否需要报错
failIfNecessary(true);
}
private void failIfNecessary(boolean firstDetectionOfTooLongFrame) {
// bytesToDiscard == 0 表示已经把要跳过的超长Frame跳过完了
if (bytesToDiscard == 0) {
// 标志位重置
long tooLongFrameLength = this.tooLongFrameLength;
this.tooLongFrameLength = 0;
discardingTooLongFrame = false;
// 这里firstDetectionOfTooLongFrame为false
// failFast默认为true,表示发现超长即报错
// 为false,要把本次超长的段全跳过skip,再报错
if (!failFast || firstDetectionOfTooLongFrame) {
fail(tooLongFrameLength);
}
} else {
// failFast 默认为true,就是不等下一次来,直接本次发现超长就报错
if (failFast && firstDetectionOfTooLongFrame) {
fail(tooLongFrameLength);
}
}
}
// Slice()方法获取一个独立读写索引的ByteBuf,共享内存区域,retain引用加一
protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
return buffer.retainedSlice(index, length);
}