<netty权威指南>笔记-以回车换行结尾的消息如何处理半包问题

来源:互联网 发布:sleep water 知乎 编辑:程序博客网 时间:2024/05/29 11:14

概述


TCP底层会发生粘包和拆包,这个是TCP的一个特性。为了减少网络数据传输的次数,TCP总是希望让网络数据到达一定量级的时候才将数据发送出去,而不是缓存区一有数据就马上发送数据。

TCP底层会根据缓冲区是否被填满了,来决定是否发送数据。但是从业务层面上看,这个是不合理的。因为一份业务数据可能很小,还不足以填满缓冲区,这样底层TCP就不会立刻把这份业务数据发送出去,而是等到好几份业务数据的大小填满缓冲区后才发送。这样子无论是服务端还是客户端,接收数据的时候可能会处理出错。

如果是以回车换行结尾的消息,那么组合使用

LineBasedFrameDecoder
StringDecoder

来处理半包问题。


TCP粘包拆包重现

李林锋的《netty权威指南》详细说明了TCP的粘包和拆包问题,下面就用netty权威指南》的例子作为demo。


服务端代码

package tcp.server;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;public class SocketServer {    public static void main(String[] args) throws InterruptedException {        EventLoopGroup parentGroup = new NioEventLoopGroup();        EventLoopGroup childGroup = new NioEventLoopGroup();        try {            ServerBootstrap serverBootstrap = new ServerBootstrap();            serverBootstrap.group(parentGroup, childGroup)                    .channel(NioServerSocketChannel.class)                    .childHandler(new SocketServerInitializer());            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();            channelFuture.channel().closeFuture().sync();        }        finally {            parentGroup.shutdownGracefully();            childGroup.shutdownGracefully();        }    }}package tcp.server;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;public class SocketServerInitializer extends ChannelInitializer<SocketChannel> {    @Override    protected void initChannel(SocketChannel ch) throws Exception {        ChannelPipeline pipeline = ch.pipeline();        pipeline.addLast(new SocketServerHandler());    }}package tcp.server;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import java.util.Date;public class SocketServerHandler extends ChannelInboundHandlerAdapter {    private int counter;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        ByteBuf buf = (ByteBuf)msg;        byte[] req = new byte[buf.readableBytes()];        //将buf中的数据读取到req中        buf.readBytes(req);        //打印客户端发送的数据        String body = new String(req, "UTF-8").substring(0, req.length - 1);        System.out.println("server receive order:"+body+";the counter is:"+ ++counter);        String currentTime = "";        if ("QUERY TIME ORDER".equalsIgnoreCase(body)) {            currentTime = new Date(System.currentTimeMillis()).toString();        }        else {            currentTime = "BAD ORDER";        }        String separator = "\n";        currentTime = currentTime + separator;        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());        ctx.writeAndFlush(resp);    }}

客户端代码

package tcp.client;import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioSocketChannel;public class SocketClient {    public static void main(String[] args) throws InterruptedException {        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();        try {            Bootstrap bootstrap = new Bootstrap();            bootstrap.group(eventLoopGroup)                    .channel(NioSocketChannel.class)                    .handler(new SocketClientInitializer());            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();            channelFuture.channel().closeFuture().sync();        }        finally {            eventLoopGroup.shutdownGracefully();        }    }}package tcp.client;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;public class SocketClientInitializer extends ChannelInitializer<SocketChannel> {    @Override    protected void initChannel(SocketChannel ch) throws Exception {        ChannelPipeline pipeline = ch.pipeline();        pipeline.addLast(new SocketClientHandler());    }}package tcp.client;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;public class SocketClientHandler extends ChannelInboundHandlerAdapter {    private int counter;    private byte[] req;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        ByteBuf buf = (ByteBuf)msg;        byte[] req = new byte[buf.readableBytes()];        buf.readBytes(req);        String body = new String(req, "UTF-8");        System.out.println("Now is : " + body + " ; the counter is :"+ ++counter);    }    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        ByteBuf message = null;        for (int i = 0; i < 100; i++) {            message = Unpooled.buffer(req.length);            message.writeBytes(req);            ctx.writeAndFlush(message);        }    }    public SocketClientHandler() {      req = ("QUERY TIME ORDER" + "\n").getBytes();    }}

当客户端与服务端建立完连接后,客户端中的SocketClientHandler类中的

channelActive

方法会被调用,往服务端发送100次请求,每个请求的内容都是

“QUERY TIME ORDER” + “\n”

当服务端接收到这些请求时,SocketServerHandler类中的

channelRead

方法会被调用,接到客户端发送的消息,如果每次收到的消息都是

QUERY TIME ORDER

那么服务端会往客户端输出当前时间。

当客户端接收到服务端的请求后,客户端类SocketClientHandler的

channelRead

方法会被调用,输出服务端返回的数据。

理论上,我们是希望客户端发送100次请求,服务端响应后,也发送100次响应。但实际上,客户端只发送了两次请求,同时服务端响应客户端的时候,只是发送了一次请求。

服务端真实打印的信息如下

server receive order:QUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUE;the counter is:1server receive order:Y TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDERQUERY TIME ORDER;the counter is:2

注意分析上面的输出,从

server receive order:QUERY TIME ORDER

到第一个

the counter is:1

中间有非常多的

QUERY TIME ORDER

说明客户端虽然发送了100次请求,但其实是累积了很多数据后,才真正的发送了一次数据。结果100次调用,最终只给服务端发送了两次请求。

既然服务端只是接收了2次请求,那么应该也给客户端发送两次请求,但是事实上只发送了一次,客户端的输出结果如下

Now is : BAD ORDERBAD ORDER ; the counter is :1

有两个

BAD ORDER

说明服务端只给客户端发送了一次请求。

到此我们得出结论,在这个例子中

客户端和服务端都发送了粘包


netty自带的解码器解决粘包和拆包问题


可以使用自带的

LineBasedFrameDecoder和StringDecoder

解码器来解决这个问题,我们稍微调整一下代码。

在SocketServerInitializer类中加入

pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());

在SocketClientInitializer类中也加入

pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());

将SocketServerHandler类channelRead方法中的

ByteBuf buf = (ByteBuf)msg;byte[] req = new byte[buf.readableBytes()];//将buf中的数据读取到req中 buf.readBytes(req); //打印客户端发送的数据 String body = new String(req, "UTF-8").substring(0, req.length - 1);

用下面一行代码替换即可

String body = (String)msg;

同样也将SocketClientHandler类中channelRead方法中的

 ByteBuf buf = (ByteBuf)msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8");

换成

String body = (String)msg;

接下来运行服务端和客户端,发现输出结果已经正常了。
服务端输出

server receive order:QUERY TIME ORDER;the counter is:1server receive order:QUERY TIME ORDER;the counter is:2server receive order:QUERY TIME ORDER;the counter is:3server receive order:QUERY TIME ORDER;the counter is:4server receive order:QUERY TIME ORDER;the counter is:5server receive order:QUERY TIME ORDER;the counter is:6server receive order:QUERY TIME ORDER;the counter is:7server receive order:QUERY TIME ORDER;the counter is:8server receive order:QUERY TIME ORDER;the counter is:9server receive order:QUERY TIME ORDER;the counter is:10server receive order:QUERY TIME ORDER;the counter is:11server receive order:QUERY TIME ORDER;the counter is:12server receive order:QUERY TIME ORDER;the counter is:13server receive order:QUERY TIME ORDER;the counter is:14server receive order:QUERY TIME ORDER;the counter is:15server receive order:QUERY TIME ORDER;the counter is:16server receive order:QUERY TIME ORDER;the counter is:17server receive order:QUERY TIME ORDER;the counter is:18server receive order:QUERY TIME ORDER;the counter is:19server receive order:QUERY TIME ORDER;the counter is:20server receive order:QUERY TIME ORDER;the counter is:21server receive order:QUERY TIME ORDER;the counter is:22server receive order:QUERY TIME ORDER;the counter is:23server receive order:QUERY TIME ORDER;the counter is:24server receive order:QUERY TIME ORDER;the counter is:25server receive order:QUERY TIME ORDER;the counter is:26server receive order:QUERY TIME ORDER;the counter is:27server receive order:QUERY TIME ORDER;the counter is:28server receive order:QUERY TIME ORDER;the counter is:29server receive order:QUERY TIME ORDER;the counter is:30server receive order:QUERY TIME ORDER;the counter is:31server receive order:QUERY TIME ORDER;the counter is:32server receive order:QUERY TIME ORDER;the counter is:33server receive order:QUERY TIME ORDER;the counter is:34server receive order:QUERY TIME ORDER;the counter is:35server receive order:QUERY TIME ORDER;the counter is:36server receive order:QUERY TIME ORDER;the counter is:37server receive order:QUERY TIME ORDER;the counter is:38server receive order:QUERY TIME ORDER;the counter is:39server receive order:QUERY TIME ORDER;the counter is:40server receive order:QUERY TIME ORDER;the counter is:41server receive order:QUERY TIME ORDER;the counter is:42server receive order:QUERY TIME ORDER;the counter is:43server receive order:QUERY TIME ORDER;the counter is:44server receive order:QUERY TIME ORDER;the counter is:45server receive order:QUERY TIME ORDER;the counter is:46server receive order:QUERY TIME ORDER;the counter is:47server receive order:QUERY TIME ORDER;the counter is:48server receive order:QUERY TIME ORDER;the counter is:49server receive order:QUERY TIME ORDER;the counter is:50server receive order:QUERY TIME ORDER;the counter is:51server receive order:QUERY TIME ORDER;the counter is:52server receive order:QUERY TIME ORDER;the counter is:53server receive order:QUERY TIME ORDER;the counter is:54server receive order:QUERY TIME ORDER;the counter is:55server receive order:QUERY TIME ORDER;the counter is:56server receive order:QUERY TIME ORDER;the counter is:57server receive order:QUERY TIME ORDER;the counter is:58server receive order:QUERY TIME ORDER;the counter is:59server receive order:QUERY TIME ORDER;the counter is:60server receive order:QUERY TIME ORDER;the counter is:61server receive order:QUERY TIME ORDER;the counter is:62server receive order:QUERY TIME ORDER;the counter is:63server receive order:QUERY TIME ORDER;the counter is:64server receive order:QUERY TIME ORDER;the counter is:65server receive order:QUERY TIME ORDER;the counter is:66server receive order:QUERY TIME ORDER;the counter is:67server receive order:QUERY TIME ORDER;the counter is:68server receive order:QUERY TIME ORDER;the counter is:69server receive order:QUERY TIME ORDER;the counter is:70server receive order:QUERY TIME ORDER;the counter is:71server receive order:QUERY TIME ORDER;the counter is:72server receive order:QUERY TIME ORDER;the counter is:73server receive order:QUERY TIME ORDER;the counter is:74server receive order:QUERY TIME ORDER;the counter is:75server receive order:QUERY TIME ORDER;the counter is:76server receive order:QUERY TIME ORDER;the counter is:77server receive order:QUERY TIME ORDER;the counter is:78server receive order:QUERY TIME ORDER;the counter is:79server receive order:QUERY TIME ORDER;the counter is:80server receive order:QUERY TIME ORDER;the counter is:81server receive order:QUERY TIME ORDER;the counter is:82server receive order:QUERY TIME ORDER;the counter is:83server receive order:QUERY TIME ORDER;the counter is:84server receive order:QUERY TIME ORDER;the counter is:85server receive order:QUERY TIME ORDER;the counter is:86server receive order:QUERY TIME ORDER;the counter is:87server receive order:QUERY TIME ORDER;the counter is:88server receive order:QUERY TIME ORDER;the counter is:89server receive order:QUERY TIME ORDER;the counter is:90server receive order:QUERY TIME ORDER;the counter is:91server receive order:QUERY TIME ORDER;the counter is:92server receive order:QUERY TIME ORDER;the counter is:93server receive order:QUERY TIME ORDER;the counter is:94server receive order:QUERY TIME ORDER;the counter is:95server receive order:QUERY TIME ORDER;the counter is:96server receive order:QUERY TIME ORDER;the counter is:97server receive order:QUERY TIME ORDER;the counter is:98server receive order:QUERY TIME ORDER;the counter is:99server receive order:QUERY TIME ORDER;the counter is:100

客户端输出

Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :1Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :2Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :3Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :4Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :5Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :6Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :7Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :8Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :9Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :10Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :11Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :12Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :13Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :14Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :15Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :16Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :17Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :18Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :19Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :20Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :21Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :22Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :23Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :24Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :25Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :26Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :27Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :28Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :29Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :30Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :31Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :32Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :33Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :34Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :35Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :36Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :37Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :38Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :39Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :40Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :41Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :42Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :43Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :44Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :45Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :46Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :47Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :48Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :49Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :50Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :51Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :52Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :53Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :54Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :55Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :56Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :57Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :58Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :59Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :60Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :61Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :62Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :63Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :64Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :65Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :66Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :67Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :68Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :69Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :70Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :71Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :72Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :73Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :74Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :75Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :76Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :77Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :78Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :79Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :80Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :81Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :82Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :83Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :84Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :85Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :86Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :87Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :88Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :89Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :90Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :91Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :92Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :93Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :94Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :95Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :96Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :97Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :98Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :99Now is : Fri Sep 01 19:48:07 CST 2017 ; the counter is :100

如此看来直接使用netty自带的解码器可以完美解决粘包问题,当然拆包问题也是使用这两个解码器就可以搞定了。非常的方便。


csdn code 路径


这个项目的源代码放置在csdn code上,欢迎访问。
netty_study

原创粉丝点击