スティッキーバッグハーフバッグの問題
1. TCP層によって送信されるバイトデータパケットには意味がなく、どの書き込みバイトを一緒にアセンブルする必要があるかが明確ではありません。
2.私たちのプログラムでは、バイト配列の一部を完全なパッケージとして逆シリアル化する必要があります。
バイト以下のバイトは逆シリアル化に失敗します
3.逆シーケンス単位として改行文字などのマークを付けることができ、通常どおり取り出すことができます。
必要なデータパケット、LineBasedFrameDecoderのプロセッサはこの原則であり、使用方法は非常に単純です。
デシリアライズされたバイト配列を送信した後、その後に改行を追加します
LineBasedFrameDecoderは、スティッキーパッケージの原理図を解決します
LineBasedFrameDecoderは、スティッキーパッケージのハーフパッケージソースコードを解決します
//LineBasedFrameDecoder逻辑是根据换行符来进行拆包的,认为一个换行符前是一个完整包
//ByteToMessageDecoder的channelRead方法
//LineBasedFrameDecoder继承了ByteToMessageDecoder
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
CodecOutputList out = CodecOutputList.newInstance();
try {
ByteBuf data = (ByteBuf) msg;
first = cumulation == null;
if (first) {
cumulation = data;
} else {
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
}
//1.对上个拦截器过来的数据进行拆包
//2.查找到一个换行符,那么是认为是一个完整的字节包,那么添加到out中
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
throw e;
} catch (Throwable t) {
throw new DecoderException(t);
} finally {
int size = out.size();
decodeWasNull = !out.insertSinceRecycled();
//将out中完整的包,交给下一个拦截器
//如果只是半包,那么out里面是空,不会触发下一个处理器
fireChannelRead(ctx, out, size);
out.recycle();
}
} else {
ctx.fireChannelRead(msg);
}
}
//ByteToMessageDecoder的callDecode方法
//主要作用是以换行符切割性读取Buff里的数据,所以如果有粘包,那么会读取多次
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try {
while (in.isReadable()) {
int outSize = out.size();
//处理当一个完整包,将完整包,交给下一个处理器处理
if (outSize > 0) {
//将完整包交给下一个处理
fireChannelRead(ctx, out, outSize);
out.clear();
if (ctx.isRemoved()) {
break;
}
outSize = 0;
}
int oldInputLength = in.readableBytes();
//读取到换行符就会停止读取,进度下一轮
decodeRemovalReentryProtection(ctx, in, out);
//省去.....
}
} catch (DecoderException e) {
throw e;
} catch (Throwable cause) {
throw new DecoderException(cause);
}
}
//ByteToMessageDecoder的fireChannelRead方法
//将一个分解的完整包,交给下一个处理器处理
static void fireChannelRead(ChannelHandlerContext ctx, CodecOutputList msgs, int numElements) {
for (int i = 0; i < numElements; i ++) {
ctx.fireChannelRead(msgs.getUnsafe(i));
}
}
//decodeRemovalReentryProtection方法内部调用
//LineBasedFrameDecoder的decode方法,自己实现拆包,根据自己换行符分割特性
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
//查找换行符的字节位置
final int eol = findEndOfLine(buffer);
if (!discarding) {
if (eol >= 0) {
final ByteBuf frame;
final int length = eol - buffer.readerIndex();
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
//一个完整包不能超过一个自己设置的一个长度,否则抛异常
if (length > maxLength) {
buffer.readerIndex(eol + delimLength);
fail(ctx, length);
return null;
}
//将一个完整包拆分出一帧,然后停止读取
if (stripDelimiter) {
frame = buffer.readRetainedSlice(length);
buffer.skipBytes(delimLength);
} else {
frame = buffer.readRetainedSlice(length + delimLength);
}
return frame;
} else {
final int length = buffer.readableBytes();
if (length > maxLength) {
discardedBytes = length;
buffer.readerIndex(buffer.writerIndex());
discarding = true;
offset = 0;
if (failFast) {
fail(ctx, "over " + discardedBytes);
}
}
return null;
}
}
}