Netty 协议设计与解析(redis 、http)

一、redis 协议

redis 消息协议 有个固定的格式, 在客户端向 redis-server 发送命令时 要遵从该消息协议

Client端代码如下: 向redis 发送命令 set name zhangsan

@Slf4j
public class NClient {
    public static void main(String[] args) {
        send();
        System.out.println("finish");
    }


    private static void send() {
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {

                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    // 换行符
                                    byte[] line = new byte[]{13,10};
                                    
                        
                                    /**
                                     * 执行redis  set name zhangsan  命令  
                                     *   [set, name, zhangsan]
                                     *   *3       表示有 长度为3 的数组
                                     *   $3       表示元素1 的长度为3
                                     *   set       
                                     *   $4       表示元素2 的长度为4
                                     *   name
                                     *   $8       表示元素3 的长度为8
                                     *   zhangsan
                                     */
                                    ByteBuf buf = ctx.alloc().buffer();
                                    buf.writeBytes("*3".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("$3".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("set".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("$4".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("name".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("$8".getBytes());
                                    buf.writeBytes(line);
                                    buf.writeBytes("zhangsan".getBytes());
                                    buf.writeBytes(line);  // 最后还有一个换行
                                    ctx.writeAndFlush(buf);
                                }
                              // 接收 redis 响应的消息
                              @Override
           					  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        ByteBuf buf = (ByteBuf) msg;
						System.out.println(buf.toString(Charset.defaultCharset()));
                              }
                            });
                        }
                    });
            // 连接 redis-server
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6379).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

Client端 发送与响应结果

12:50:26.335 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9579b508, L:/127.0.0.1:49624 - R:localhost/127.0.0.1:6379] WRITE: 37B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 2a 33 0d 0a 24 33 0d 0a 73 65 74 0d 0a 24 34 0d |*3..$3..set..$4.|
|00000010| 0a 6e 61 6d 65 0d 0a 24 38 0d 0a 7a 68 61 6e 67 |.name..$8..zhang|
|00000020| 73 61 6e 0d 0a                                  |san..           |
+--------+-------------------------------------------------+----------------+
12:50:26.335 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9579b508, L:/127.0.0.1:49624 - R:localhost/127.0.0.1:6379] FLUSH
12:50:26.337 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9579b508, L:/127.0.0.1:49624 - R:localhost/127.0.0.1:6379] READ: 5B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 2b 4f 4b 0d 0a                                  |+OK..           |
+--------+-------------------------------------------------+----------------+
+OK

12:50:26.337 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9579b508, L:/127.0.0.1:49624 - R:localhost/127.0.0.1:6379] READ COMPLETE

通过redis-cli 查看 写入结果

.net redis封装 redis netty_2d

二、http 协议

http协议的编解码 ,Netty已经帮我们封装好了: HttpServerCodec 类。

HttpServerCodec 可处理 请求解码 和 响应编码

1.请求解码

Server端 加入 HttpServerCodec 编码处理器

@Slf4j
public class NServer {
    public static void main(String[] args) {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {

                            // 添加日志处理器
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));

                            // 添加 Http协议编码器
                            ch.pipeline().addLast(new HttpServerCodec());

                            // 添加自定义处理器  打印消息类型
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    log.debug("{}",msg.getClass());
                                }
                            });
                        }
                    });

            ChannelFuture channelFuture = bootstrap.bind(8080).sync();

            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

浏览器 访问 http://localhost:8080/index.html

Server端收到以下请求结果:

22:08:53.194 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9114a1c2, L:/127.0.0.1:8080 - R:/127.0.0.1:64467] READ: 696B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 69 6e 64 65 78 2e 68 74 6d 6c 20 |GET /index.html |
|00000010| 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 |HTTP/1.1..Host: |
|00000020| 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 38 30 0d 0a |localhost:8080..|
|00000030| 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 |Connection: keep|
|00000040| 2d 61 6c 69 76 65 0d 0a 43 61 63 68 65 2d 43 6f |-alive..Cache-Co|
|00000050| 6e 74 72 6f 6c 3a 20 6d 61 78 2d 61 67 65 3d 30 |ntrol: max-age=0|
|00000060| 0d 0a 73 65 63 2d 63 68 2d 75 61 3a 20 22 47 6f |..sec-ch-ua: "Go|
|00000070| 6f 67 6c 65 20 43 68 72 6f 6d 65 22 3b 76 3d 22 |ogle Chrome";v="|
|00000080| 39 33 22 2c 20 22 20 4e 6f 74 3b 41 20 42 72 61 |93", " Not;A Bra|
|00000090| 6e 64 22 3b 76 3d 22 39 39 22 2c 20 22 43 68 72 |nd";v="99", "Chr|
|000000a0| 6f 6d 69 75 6d 22 3b 76 3d 22 39 33 22 0d 0a 73 |omium";v="93"..s|
|000000b0| 65 63 2d 63 68 2d 75 61 2d 6d 6f 62 69 6c 65 3a |ec-ch-ua-mobile:|
|000000c0| 20 3f 30 0d 0a 73 65 63 2d 63 68 2d 75 61 2d 70 | ?0..sec-ch-ua-p|
|000000d0| 6c 61 74 66 6f 72 6d 3a 20 22 57 69 6e 64 6f 77 |latform: "Window|
|000000e0| 73 22 0d 0a 55 70 67 72 61 64 65 2d 49 6e 73 65 |s"..Upgrade-Inse|
|000000f0| 63 75 72 65 2d 52 65 71 75 65 73 74 73 3a 20 31 |cure-Requests: 1|
|00000100| 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f |..User-Agent: Mo|
|00000110| 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f |zilla/5.0 (Windo|
|00000120| 77 73 20 4e 54 20 31 30 2e 30 3b 20 57 69 6e 36 |ws NT 10.0; Win6|
|00000130| 34 3b 20 78 36 34 29 20 41 70 70 6c 65 57 65 62 |4; x64) AppleWeb|
|00000140| 4b 69 74 2f 35 33 37 2e 33 36 20 28 4b 48 54 4d |Kit/537.36 (KHTM|
|00000150| 4c 2c 20 6c 69 6b 65 20 47 65 63 6b 6f 29 20 43 |L, like Gecko) C|
|00000160| 68 72 6f 6d 65 2f 39 33 2e 30 2e 34 35 37 37 2e |hrome/93.0.4577.|
|00000170| 36 33 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 |63 Safari/537.36|
|00000180| 0d 0a 41 63 63 65 70 74 3a 20 74 65 78 74 2f 68 |..Accept: text/h|
|00000190| 74 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f |tml,application/|
|000001a0| 78 68 74 6d 6c 2b 78 6d 6c 2c 61 70 70 6c 69 63 |xhtml+xml,applic|
|000001b0| 61 74 69 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 2c |ation/xml;q=0.9,|
|000001c0| 69 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 |image/avif,image|
|000001d0| 2f 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 |/webp,image/apng|
|000001e0| 2c 2a 2f 2a 3b 71 3d 30 2e 38 2c 61 70 70 6c 69 |,*/*;q=0.8,appli|
|000001f0| 63 61 74 69 6f 6e 2f 73 69 67 6e 65 64 2d 65 78 |cation/signed-ex|
|00000200| 63 68 61 6e 67 65 3b 76 3d 62 33 3b 71 3d 30 2e |change;v=b3;q=0.|
|00000210| 39 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 |9..Sec-Fetch-Sit|
|00000220| 65 3a 20 6e 6f 6e 65 0d 0a 53 65 63 2d 46 65 74 |e: none..Sec-Fet|
|00000230| 63 68 2d 4d 6f 64 65 3a 20 6e 61 76 69 67 61 74 |ch-Mode: navigat|
|00000240| 65 0d 0a 53 65 63 2d 46 65 74 63 68 2d 55 73 65 |e..Sec-Fetch-Use|
|00000250| 72 3a 20 3f 31 0d 0a 53 65 63 2d 46 65 74 63 68 |r: ?1..Sec-Fetch|
|00000260| 2d 44 65 73 74 3a 20 64 6f 63 75 6d 65 6e 74 0d |-Dest: document.|
|00000270| 0a 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 |.Accept-Encoding|
|00000280| 3a 20 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 2c |: gzip, deflate,|
|00000290| 20 62 72 0d 0a 41 63 63 65 70 74 2d 4c 61 6e 67 | br..Accept-Lang|
|000002a0| 75 61 67 65 3a 20 7a 68 2d 43 4e 2c 7a 68 3b 71 |uage: zh-CN,zh;q|
|000002b0| 3d 30 2e 39 0d 0a 0d 0a                         |=0.9....        |
+--------+-------------------------------------------------+----------------+
22:08:53.209 [nioEventLoopGroup-3-1] DEBUG nettydemo.plaster.NServer - class io.netty.handler.codec.http.DefaultHttpRequest
22:08:53.209 [nioEventLoopGroup-3-1] DEBUG nettydemo.plaster.NServer - class io.netty.handler.codec.http.LastHttpContent$1

由结果 可见 该HTTP 解码器 将请求 解析成了两个类 DefaultHttpRequest(HttpRequest)LastHttpContent(HttpContent)

如果我们接下来 想针对这两个类做特殊处理可以使用 SimpleChannelInboundHandler<针对类的类名> 处理器

ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {

                            // 添加日志处理器
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));

                            // 添加 Http协议编码器
                            ch.pipeline().addLast(new HttpServerCodec());

                            // 根据 消息解析后序列化出来的的 类型  来做特殊处理
                            // 这里处理 HttpRequest类
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
                                    // 获取请求
                                    log.debug(msg.uri());
                                }
                            });
                        }
                    });

2.响应编码

Server端代码

@Slf4j
public class NServer {
    public static void main(String[] args) {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {

                            // 添加日志处理器
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));

                            // 添加 Http协议编码器
                            ch.pipeline().addLast(new HttpServerCodec());

                            // 根据 消息解析后序列化出来的的 类型  来做特殊处理
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
                                    // 获取请求
                                    log.debug(msg.uri());

                                    //返回响应
                                    DefaultFullHttpResponse response =
                                            new DefaultFullHttpResponse(msg.protocolVersion(),HttpResponseStatus.OK);

                                    response.content().writeBytes("<h1> Hello,World!<h1>".getBytes());
                                    //返回响应
                                    ctx.writeAndFlush(response);
                                }
                            });
                        }
                    });

            ChannelFuture channelFuture = bootstrap.bind(8080).sync();

            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

浏览器 访问 http://localhost:8080/index.html

Server端日志打印:

22:22:14.836 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x73471bf7, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:59442] READ: 696B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 69 6e 64 65 78 2e 68 74 6d 6c 20 |GET /index.html |
|00000010| 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 |HTTP/1.1..Host: |
|00000020| 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 38 30 0d 0a |localhost:8080..|
|00000030| 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 |Connection: keep|
|00000040| 2d 61 6c 69 76 65 0d 0a 43 61 63 68 65 2d 43 6f |-alive..Cache-Co|
|00000050| 6e 74 72 6f 6c 3a 20 6d 61 78 2d 61 67 65 3d 30 |ntrol: max-age=0|
|00000060| 0d 0a 73 65 63 2d 63 68 2d 75 61 3a 20 22 47 6f |..sec-ch-ua: "Go|
|00000070| 6f 67 6c 65 20 43 68 72 6f 6d 65 22 3b 76 3d 22 |ogle Chrome";v="|
|00000080| 39 33 22 2c 20 22 20 4e 6f 74 3b 41 20 42 72 61 |93", " Not;A Bra|
|00000090| 6e 64 22 3b 76 3d 22 39 39 22 2c 20 22 43 68 72 |nd";v="99", "Chr|
|000000a0| 6f 6d 69 75 6d 22 3b 76 3d 22 39 33 22 0d 0a 73 |omium";v="93"..s|
|000000b0| 65 63 2d 63 68 2d 75 61 2d 6d 6f 62 69 6c 65 3a |ec-ch-ua-mobile:|
|000000c0| 20 3f 30 0d 0a 73 65 63 2d 63 68 2d 75 61 2d 70 | ?0..sec-ch-ua-p|
|000000d0| 6c 61 74 66 6f 72 6d 3a 20 22 57 69 6e 64 6f 77 |latform: "Window|
|000000e0| 73 22 0d 0a 55 70 67 72 61 64 65 2d 49 6e 73 65 |s"..Upgrade-Inse|
|000000f0| 63 75 72 65 2d 52 65 71 75 65 73 74 73 3a 20 31 |cure-Requests: 1|
|00000100| 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f |..User-Agent: Mo|
|00000110| 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f |zilla/5.0 (Windo|
|00000120| 77 73 20 4e 54 20 31 30 2e 30 3b 20 57 69 6e 36 |ws NT 10.0; Win6|
|00000130| 34 3b 20 78 36 34 29 20 41 70 70 6c 65 57 65 62 |4; x64) AppleWeb|
|00000140| 4b 69 74 2f 35 33 37 2e 33 36 20 28 4b 48 54 4d |Kit/537.36 (KHTM|
|00000150| 4c 2c 20 6c 69 6b 65 20 47 65 63 6b 6f 29 20 43 |L, like Gecko) C|
|00000160| 68 72 6f 6d 65 2f 39 33 2e 30 2e 34 35 37 37 2e |hrome/93.0.4577.|
|00000170| 36 33 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 |63 Safari/537.36|
|00000180| 0d 0a 41 63 63 65 70 74 3a 20 74 65 78 74 2f 68 |..Accept: text/h|
|00000190| 74 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f |tml,application/|
|000001a0| 78 68 74 6d 6c 2b 78 6d 6c 2c 61 70 70 6c 69 63 |xhtml+xml,applic|
|000001b0| 61 74 69 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 2c |ation/xml;q=0.9,|
|000001c0| 69 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 |image/avif,image|
|000001d0| 2f 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 |/webp,image/apng|
|000001e0| 2c 2a 2f 2a 3b 71 3d 30 2e 38 2c 61 70 70 6c 69 |,*/*;q=0.8,appli|
|000001f0| 63 61 74 69 6f 6e 2f 73 69 67 6e 65 64 2d 65 78 |cation/signed-ex|
|00000200| 63 68 61 6e 67 65 3b 76 3d 62 33 3b 71 3d 30 2e |change;v=b3;q=0.|
|00000210| 39 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 |9..Sec-Fetch-Sit|
|00000220| 65 3a 20 6e 6f 6e 65 0d 0a 53 65 63 2d 46 65 74 |e: none..Sec-Fet|
|00000230| 63 68 2d 4d 6f 64 65 3a 20 6e 61 76 69 67 61 74 |ch-Mode: navigat|
|00000240| 65 0d 0a 53 65 63 2d 46 65 74 63 68 2d 55 73 65 |e..Sec-Fetch-Use|
|00000250| 72 3a 20 3f 31 0d 0a 53 65 63 2d 46 65 74 63 68 |r: ?1..Sec-Fetch|
|00000260| 2d 44 65 73 74 3a 20 64 6f 63 75 6d 65 6e 74 0d |-Dest: document.|
|00000270| 0a 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 |.Accept-Encoding|
|00000280| 3a 20 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 2c |: gzip, deflate,|
|00000290| 20 62 72 0d 0a 41 63 63 65 70 74 2d 4c 61 6e 67 | br..Accept-Lang|
|000002a0| 75 61 67 65 3a 20 7a 68 2d 43 4e 2c 7a 68 3b 71 |uage: zh-CN,zh;q|
|000002b0| 3d 30 2e 39 0d 0a 0d 0a                         |=0.9....        |
+--------+-------------------------------------------------+----------------+
22:22:14.851 [nioEventLoopGroup-3-1] DEBUG nettydemo.plaster.NServer - /index.html
22:22:14.855 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x73471bf7, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:59442] WRITE: 40B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 0d 0a 3c 68 31 3e 20 48 65 6c 6c 6f 2c 57 6f |...<h1> Hello,Wo|
|00000020| 72 6c 64 21 3c 68 31 3e                         |rld!<h1>        |
+--------+-------------------------------------------------+----------------+
22:22:14.855 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x73471bf7, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:59442] FLUSH

浏览器结果:

.net redis封装 redis netty_.net redis封装_02

由浏览器响应结果 可看见左上角 一直在转圈 , 这是因为我们服务端没有告诉浏览器 返回响应的长度是多少,所以浏览器在一直不停的转圈等待(误以为还有更多的响应会到来)。

因此 我们要在响应头 中告知浏览器 返回的响应长度是多少。

修改代码如下:

// 根据 消息解析后序列化出来的的 类型  来做特殊处理
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
                                    // 获取请求
                                    log.debug(msg.uri());

                                    //返回响应
                                    DefaultFullHttpResponse response =
                                            new DefaultFullHttpResponse(msg.protocolVersion(),HttpResponseStatus.OK);

                                    // 加上响应长度
                                    byte[] bytes = "<h1> Hello,World!<h1>".getBytes();
                                    response.headers().setInt(CONTENT_LENGTH,bytes.length);
                                    response.content().writeBytes(bytes);

                                    //返回响应
                                    ctx.writeAndFlush(response);
                                }
                            });

万般皆下品,唯有读书高!