Netty入門(十)解碼分隔符和基於長度的協議


  我們需要區分不同幀的首尾,通常需要在結尾設定特定分隔符或者在首部添加長度字段,分別稱為分隔符協議和基於長度的協議,本節講解 Netty 如何解碼這些協議。

一、分隔符協議

  Netty 附帶的解碼器可以很容易的提取一些序列分隔:

  

  下面顯示了使用 “\r\n”分隔符的處理:

  

  下面為 LineBaseFrameDecoder 的簡單實現:

 1 public class CmdHandlerInitializer extends ChannelInitializer<Channel> {
 2 
 3     @Override
 4     protected void initChannel(Channel ch) throws Exception {
 5         ChannelPipeline pipeline = ch.pipeline();
 6         // 添加解碼器,
 7         pipeline.addLast(new CmdDecoder(65 * 1024));
 8         pipeline.addLast(new CmdHandler());
 9     }
10 
11     public static final class Cmd {
12         private final ByteBuf name;    // 名字
13         private final ByteBuf args;    // 參數
14         
15         public Cmd(ByteBuf name, ByteBuf args) {
16             this.name = name;
17             this.args = args;
18         }
19         
20         public ByteBuf name() {
21             return name;
22         }
23         
24         public ByteBuf args() {
25             return args;
26         }
27     }
28     
29     /**
30      * 根據分隔符將消息解碼成Cmd對象傳給下一個處理器
31      */
32     public static final class CmdDecoder extends LineBasedFrameDecoder {
33 
34         public CmdDecoder(int maxLength) {
35             super(maxLength);
36         }
37         
38         @Override
39         protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
40             // 通過結束分隔符從 ByteBuf 提取幀
41             ByteBuf frame = (ByteBuf)super.decode(ctx, buffer);
42             if(frame == null)
43                 return null;
44             int index = frame.indexOf(frame.readerIndex(), frame.writerIndex(), (byte)' ');
45             // 提取 Cmd 對象
46             return new Cmd(frame.slice(frame.readerIndex(), index), 
47                     frame.slice(index+1, frame.writerIndex()));
48         }
49     }
50     
51     public static final class CmdHandler extends SimpleChannelInboundHandler<Cmd> {
52 
53         @Override
54         protected void channelRead0(ChannelHandlerContext ctx, Cmd msg) throws Exception {
55             // 處理 Cmd 信息
56         }
57         
58     }
59 }

 

   上面的例子主要實現了利用換行符‘\n’分隔幀,然后將每行數據解碼成一個 Cmd 實例。

 

二、基於長度的協議 

   基於長度的協議在幀頭定義了一個幀編碼的長度,而不是在結束位置用一個特殊的分隔符來標記。Netty 提供了兩種編碼器,用於處理這種類型的協議,如下:

  

   FixedLengthFrameDecoder 的操作是提取固定長度每幀 8 字節,如下圖所示:

  

  但大部分時候,我們會把幀的大小編碼在頭部,這種情況可以使用 LengthFieldBaseFrameDecoder,它會提取幀的長度並根據長度讀取幀的數據部分,如下:

  

  下面是 LengthFieldBaseFrameDecoder 的一個簡單應用:

 1 /**
 2  * 基於長度的協議
 3  * LengthFieldBasedFrameDecoder
 4  */
 5 public class LineBasedHandlerInitializer extends ChannelInitializer<Channel> {
 6 
 7     @Override
 8     protected void initChannel(Channel ch) throws Exception {
 9         ChannelPipeline pipeline = ch.pipeline();
10         // 用於提取基於幀編碼長度8個字節的幀
11         pipeline.addLast(new LengthFieldBasedFrameDecoder(65*1024, 0, 8));
12         pipeline.addLast(new FrameHandler());
13     }
14     
15     public static final class FrameHandler extends SimpleChannelInboundHandler<ByteBuf> {
16 
17         @Override
18         protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
19             // TODO 數據處理
20         }
21         
22     }
23 
24 }

 

   上面的例子主要實現了提取幀首部 8 字節的長度,然后提取數據部分進行處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM