自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Netty - 粘包和半包(下)

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
上一篇介紹了粘包和半包及其通用的解決方案,今天重點來看一下 Netty 是如何實現(xiàn)封裝成幀(Framing)方案的。

接上篇《TCP 粘包和半包 介紹及解決(上)

上一篇介紹了粘包和半包及其通用的解決方案,今天重點來看一下 Netty 是如何實現(xiàn)封裝成幀(Framing)方案的。

解碼核心流程

之前介紹過三種解碼器FixedLengthFrameDecoder、DelimiterBasedFrameDecoder、LengthFieldBasedFrameDecoder,它們都繼承自ByteToMessageDecoder,而ByteToMessageDecoder繼承自ChannelInboundHandlerAdapter,其核心方法為channelRead。因此,我們來看看ByteToMessageDecoder的channelRead方法:

  1. @Override 
  2. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  3.  if (msg instanceof ByteBuf) { 
  4.  CodecOutputList out = CodecOutputList.newInstance(); 
  5.  try { 
  6.  // 將傳入的消息轉(zhuǎn)化為data 
  7.  ByteBuf data = (ByteBuf) msg; 
  8.  // 最終實現(xiàn)的目標(biāo)是將數(shù)據(jù)全部放進(jìn)cumulation中 
  9.  first = cumulation == null; 
  10.  // 第一筆數(shù)據(jù)直接放入 
  11.  if (first) { 
  12.  cumulation = data
  13.  } else { 
  14.  // 不是第一筆數(shù)據(jù)就進(jìn)行追加 
  15.  cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); 
  16.  } 
  17.  // 解碼 
  18.  callDecode(ctx, cumulation, out); 
  19.  } 
  20.  // 以下代碼省略,因為不屬于解碼過程 
  21.  } 

再來看看callDecode方法:

  1. protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { 
  2.  try { 
  3.  while (in.isReadable()) { 
  4.  int outoutSize = out.size(); 
  5.  if (outSize > 0) { 
  6.  // 以下代碼省略,因為初始狀態(tài)時,outSize 只可能是0,不可能進(jìn)入這里 
  7.  } 
  8.  int oldInputLength = in.readableBytes(); 
  9.  // 在進(jìn)行 decode 時,不執(zhí)行handler的remove操作。 
  10.  // 只有當(dāng) decode 執(zhí)行完之后,開始清理數(shù)據(jù)。 
  11.  decodeRemovalReentryProtection(ctx, in, out); 
  12.  // 省略以下代碼,因為后面的內(nèi)容也不是解碼的過程 

再來看看decodeRemovalReentryProtection方法:

  1. final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) 
  2.  throws Exception { 
  3.  // 設(shè)置當(dāng)前狀態(tài)為正在解碼 
  4.  decodeState = STATE_CALLING_CHILD_DECODE
  5.  try { 
  6.  // 解碼 
  7.  decode(ctx, in, out); 
  8.  } finally { 
  9.  // 執(zhí)行hander的remove操作 
  10.  boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING; 
  11.  decodeState = STATE_INIT
  12.  if (removePending) { 
  13.  handlerRemoved(ctx); 
  14.  } 
  15.  } 
  16. // 子類都重寫了該方法,每種實現(xiàn)都會有自己特殊的解碼方式 
  17. protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception; 

從上面的過程可以總結(jié)出,在解碼之前,需要先將數(shù)據(jù)寫入cumulation,當(dāng)解碼結(jié)束后,需要通過 handler 進(jìn)行移除。

具體解碼過程

剛剛說到decode方法在子類中都有實現(xiàn),那針對我們說的三種解碼方式,一一看其實現(xiàn)。

1. FixedLengthFrameDecoder

其源碼為:

  1. @Override 
  2. protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 
  3.  Object decodedecoded = decode(ctx, in); 
  4.  if (decoded != null) { 
  5.  out.add(decoded); 
  6.  } 
  7. protected Object decode( 
  8.  @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { 
  9.  // 收集到的數(shù)據(jù)是否小于固定長度,小于就代表無法解析 
  10.  if (in.readableBytes() < frameLength) { 
  11.  return null; 
  12.  } else { 
  13.  return in.readRetainedSlice(frameLength); 
  14.  } 

就和這個類的名字一樣簡單,就是固定長度進(jìn)行解碼,因此,在設(shè)置該解碼器的時候,需要在構(gòu)造方式里傳入frameLength。

2. DelimiterBasedFrameDecoder

其源碼為:

  1. @Override 
  2. protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 
  3.  Object decodedecoded = decode(ctx, in); 
  4.  if (decoded != null) { 
  5.  out.add(decoded); 
  6.  } 
  7. protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { 
  8.  // 當(dāng)前的分割符是否是換行分割符(\n或者\r\n) 
  9.  if (lineBasedDecoder != null) { 
  10.  return lineBasedDecoder.decode(ctx, buffer); 
  11.  } 
  12.  // Try all delimiters and choose the delimiter which yields the shortest frame. 
  13.  int minFrameLength = Integer.MAX_VALUE; 
  14.  ByteBuf minDelim = null
  15.  // 其他分割符進(jìn)行一次切分 
  16.  for (ByteBuf delim: delimiters) { 
  17.  int frameLength = indexOf(buffer, delim); 
  18.  if (frameLength >= 0 && frameLength < minFrameLength) { 
  19.  minFrameLength = frameLength
  20.  minDelim = delim
  21.  } 
  22.  } 
  23.  // 以下代碼省略 

根據(jù)它的名字可以知道,分隔符才是它的核心。它將分割符分成兩類,只有換行分割符(n或者rn)和其他。因此,需要注意的是,你可以定義多種分割符,它都是支持的。

3. LengthFieldBasedFrameDecoder

該類比較復(fù)雜,如果直接看方法容易把自己看混亂,因此我準(zhǔn)備結(jié)合類上的解釋,先看看其私有變量。

2 bytes length field at offset 1 in the middle of 4 bytes header, strip the first header field and the length field, the length field represents the length of the whole message

Let's give another twist to the previous example. The only difference from the previous example is that the length field represents the length of the whole message instead of the message body, just like the third example. We have to count the length of HDR1 and Length into lengthAdjustment. Please note that we don't need to take the length of HDR2 into account because the length field already includes the whole header length.

  1. * BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) 
  2. * +------+--------+------+----------------+ +------+----------------+ 
  3. * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content | 
  4. * | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" | 
  5. * +------+--------+------+----------------+ +------+----------------+ 
  • lengthFieldOffset :該字段代表 Length 字段是從第幾個字節(jié)開始的。上面的例子里,Length 字段是從第1個字節(jié)開始(HDR1 是第0個字節(jié)),因此該值即為0。
  • lengthFieldLength:該字段代表 Length 字段所占用的字節(jié)數(shù)。上面的例子里,Length 字段占用2個字節(jié),因此該值為2。
  • lengthAdjustment:該字段代表 Length 字段結(jié)束位置到真正的內(nèi)容開始位置的距離。上面例子里,因為 Length 字段的含義是整個消息(包括 HDR1、Length、HDR2、Actual Content,一般 Length 指的只是 Actual Content),所以 Length 末尾到真正的內(nèi)容開始位置(HDR1的開始處),相當(dāng)于減少3個字節(jié),所以是-3。
  • initialBytesToStrip: 展示時需要從 Length 字段末尾開始跳過幾個字節(jié)。上面例子里,因為真正的內(nèi)容是從 HDR1 開始的,最終展示的內(nèi)容是從 HDR2 開始的,所以中間差了3個字節(jié),所以該值是3。

該類的解碼方法比較復(fù)雜,有興趣的同學(xué)可以試著自己分析一下。

總結(jié)

 

 

這一篇主要是結(jié)合 Netty 里的源代碼講解了 Netty 中封裝成幀(Framing)的三種方式,相信你一定有了不一樣的理解。

 

責(zé)任編輯:趙寧寧 來源: 健程之道
相關(guān)推薦

2024-08-16 21:47:18

2019-10-24 07:35:13

TCP粘包Netty

2022-08-01 07:07:15

粘包半包封裝

2021-07-15 10:35:16

NettyTCPJava

2025-04-10 10:15:30

2020-10-15 18:31:36

理解Netty編解碼

2024-12-19 11:00:00

TCP網(wǎng)絡(luò)通信粘包

2021-01-13 10:18:29

SocketNetty粘包

2024-06-03 08:09:46

2022-05-23 08:35:43

粘包半包數(shù)據(jù)

2019-10-17 11:06:32

TCP粘包通信協(xié)議

2020-01-06 15:23:41

NettyTCP粘包

2019-11-12 15:15:30

網(wǎng)絡(luò)安全網(wǎng)絡(luò)安全技術(shù)周刊

2020-03-10 08:27:24

TCP粘包網(wǎng)絡(luò)協(xié)議

2024-10-12 18:16:27

2021-03-09 22:30:47

TCP拆包協(xié)議

2022-04-28 08:38:09

TCP協(xié)議解碼器

2021-08-13 09:06:52

Go高性能優(yōu)化

2021-10-08 09:38:57

NettyChannelHand架構(gòu)

2020-12-23 07:53:01

TCP通信Netty
點贊
收藏

51CTO技術(shù)棧公眾號