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 字节的长度,然后提取数据部分进行处理。

 

posted @ 2018-05-15 14:37 Just_for_Myself 阅读(...) 评论(...) 编辑 收藏