LineBasedFrameDecoder solves the source code analysis of sticky package and half package

Sticky bag half bag problem

1. The byte data packet transmitted by the TCP layer has no meaning, and it is not clear which write bytes need to be assembled together.

2. In our program, it is generally necessary to deserialize a part of the byte array as a complete package, if there is more

Bytes or one less byte will fail to deserialize

3. We can make a mark, such as a newline character as a reverse sequence unit, then we can be taken out normally

The required data packet, the processor of LineBasedFrameDecoder is this principle, the method of use is very simple, as long as

After sending a deserialized byte array, add a newline after it

LineBasedFrameDecoder solves the principle diagram of sticky package

Insert picture description here

LineBasedFrameDecoder solves sticky package half package source code

//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;
            }
        } 
    }

Guess you like

Origin blog.csdn.net/weixin_38312719/article/details/108763753