netty源码分析之LengthFieldBasedFrameDecoder
1 /** 2 * Creates a new instance. 3 * 4 * @param byteOrder 5 * the {@link ByteOrder} of the length field 6 * @param maxFrameLength 包的最大长度 7 * the maximum length of the frame. If the length of the frame is 8 * greater than this value, {@link TooLongFrameException} will be 9 * thrown. 10 * @param lengthFieldOffset 长度域的偏移量 11 * the offset of the length field 12 * @param lengthFieldLength 长度域长度 13 * the length of the length field 14 * @param lengthAdjustment 包体长度调整的大小(可以为负数) 15 * the compensation value to add to the value of the length field 16 * @param initialBytesToStrip 跳过(忽略)的字节数 17 * the number of first bytes to strip out from the decoded frame 18 * @param failFast 19 * If <tt>true</tt>, a {@link TooLongFrameException} is thrown as 20 * soon as the decoder notices the length of the frame will exceed 21 * <tt>maxFrameLength</tt> regardless of whether the entire frame 22 * has been read. If <tt>false</tt>, a {@link TooLongFrameException} 23 * is thrown after the entire frame that exceeds <tt>maxFrameLength</tt> 24 * has been read. 25 */ 26 public LengthFieldBasedFrameDecoder( 27 ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, 28 int lengthAdjustment, int initialBytesToStrip, boolean failFast) { 29 if (byteOrder == null) { 30 throw new NullPointerException("byteOrder"); 31 } 32 33 checkPositive(maxFrameLength, "maxFrameLength"); 34 35 checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset"); 36 37 checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip"); 38 39 if (lengthFieldOffset > maxFrameLength - lengthFieldLength) { 40 throw new IllegalArgumentException( 41 "maxFrameLength (" + maxFrameLength + ") " + 42 "must be equal to or greater than " + 43 "lengthFieldOffset (" + lengthFieldOffset + ") + " + 44 "lengthFieldLength (" + lengthFieldLength + ")."); 45 } 46 47 this.byteOrder = byteOrder; 48 this.maxFrameLength = maxFrameLength; 49 this.lengthFieldOffset = lengthFieldOffset; 50 this.lengthFieldLength = lengthFieldLength; 51 this.lengthAdjustment = lengthAdjustment; 52 lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength; 53 this.initialBytesToStrip = initialBytesToStrip; 54 this.failFast = failFast; 55 }
示例:
|p_h|len|cmd|data|sum|p_t| len是整个包长度
socketChannel.pipeline().addLast("lengthFieldBasedFrameDecoder", new LengthFieldBasedFrameDecoder(14, 1, 1, -2, 0));
1 /** 2 * Create a frame out of the {@link ByteBuf} and return it. 3 * 4 * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to 5 * @param in the {@link ByteBuf} from which to read data 6 * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could 7 * be created. 8 */ 9 protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { 10 if (discardingTooLongFrame) { //丢弃模式 11 discardingTooLongFrame(in); 12 } 13 14 if (in.readableBytes() < lengthFieldEndOffset) { //如果当前可读字节还未达到长度域的偏移,那说明肯定是读不到长度域的,直接不读 15 return null; 16 } 17 18 int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset; //通过当前读索引+长度域偏移量,获取长度域的实际字节索引 19 long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder); //通过长度域的实际字节索引和长度域长度,获取实际包长度 20 21 if (frameLength < 0) { 22 failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset); 23 } 24 25 frameLength += lengthAdjustment + lengthFieldEndOffset; //调整包长度,如果长度是指整个包的长度,lengthAdjustment为lengthFieldEndOffset的负数;如果长度是指包内容大小,lengthAdjustment设置为0。ps:数据包是否需要调整需要看具体业务(lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength) 26 27 if (frameLength < lengthFieldEndOffset) { 28 failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset); 29 } 30 31 if (frameLength > maxFrameLength) { //长度大于最大长度时,判断是直接丢弃数据还是进入丢弃模式 32 exceededFrameLength(in, frameLength); 33 return null; 34 } 35 36 // never overflows because it's less than maxFrameLength 37 int frameLengthInt = (int) frameLength; 38 if (in.readableBytes() < frameLengthInt) { //可读字节数小于长度,直接不读,等待下次读取 39 return null; 40 } 41 42 if (initialBytesToStrip > frameLengthInt) { //需要跳过的字节数大于长度,报错 43 failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip); 44 } 45 in.skipBytes(initialBytesToStrip); //跳过指定字节数 46 47 // extract frame 48 int readerIndex = in.readerIndex(); //当前累积数据的读指针 49 int actualFrameLength = frameLengthInt - initialBytesToStrip; //拿到待抽取数据包的实际长度 50 ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength); //抽取数据包 51 in.readerIndex(readerIndex + actualFrameLength); //移动读指针 52 return frame; 53 }
判断是直接丢弃数据还是进入丢弃模式
1 private void exceededFrameLength(ByteBuf in, long frameLength) { 2 long discard = frameLength - in.readableBytes(); //包长度与可读字节数比较 3 tooLongFrameLength = frameLength; 4 5 if (discard < 0) { 6 // buffer contains more bytes then the frameLength so we can discard all now 7 in.skipBytes((int) frameLength); //可读字节数大,直接丢弃包长度,剩下的可能是个合法数据包 8 } else { 9 // Enter the discard mode and discard everything received so far. 10 discardingTooLongFrame = true; //包长度大,进入丢弃模式 11 bytesToDiscard = discard; //剩余需要丢弃的字节长度 12 in.skipBytes(in.readableBytes()); //跳过可读字节数 13 } 14 failIfNecessary(true); //判断是否需要抛出异常 15 }
丢弃模式
1 private void discardingTooLongFrame(ByteBuf in) { 2 long bytesToDiscard = this.bytesToDiscard; 3 int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes()); //需要丢弃的字节长度与可读字节数获取最小值 4 in.skipBytes(localBytesToDiscard); //跳过字节 5 bytesToDiscard -= localBytesToDiscard; //获取剩余需要丢弃的字节数 6 this.bytesToDiscard = bytesToDiscard; 7 8 failIfNecessary(false); 9 }
判断是否需要抛出异常
private void failIfNecessary(boolean firstDetectionOfTooLongFrame) { if (bytesToDiscard == 0) { //需要丢弃字节数为0时,退出丢弃模式 // Reset to the initial state and tell the handlers that // the frame was too large. long tooLongFrameLength = this.tooLongFrameLength; this.tooLongFrameLength = 0; discardingTooLongFrame = false; if (!failFast || firstDetectionOfTooLongFrame) { fail(tooLongFrameLength); } } else { // Keep discarding and notify handlers if necessary. if (failFast && firstDetectionOfTooLongFrame) { //failFast默认true fail(tooLongFrameLength); //报错 } } }
参考:
分类:
netty
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?