首页 > 其他分享 >Netty进阶-黏包半包分析

Netty进阶-黏包半包分析

时间:2022-10-24 15:14:37浏览次数:49  
标签:Netty 38 32 黏包 30 36 new 半包 31

Netty进阶

1、黏包

服务端

//测试黏包服务端
@Slf4j
public class TestNianbaoServer {
    public static void main(String[] args) {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();

        try {
            ChannelFuture channelFuture = new ServerBootstrap()
                    .group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new LoggingHandler());
                        }
                    })
                    .bind(8080).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("server error");
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

客户端

//黏包测试客户端
public class TestNianbaoClient {
    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();

        try {
            ChannelFuture channelFuture = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                //连接建立成功,active事件
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    for (int i = 0; i < 10; i++) {
                                        ByteBuf buf = ctx.alloc().buffer();
                                        buf.writeBytes("0123456789".getBytes());
                                        ctx.writeAndFlush(buf);
                                    }
                                }
                            });
                        }
                    })
                    .connect(new InetSocketAddress(8080)).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            group.shutdownGracefully();
        }
    }
}

结果

12:46:22.133 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9546fc94, L:/192.168.1.91:8080 - R:/192.168.1.91:11527] READ: 100B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 |0123456789012345|
|00000010| 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 |6789012345678901|
|00000020| 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 |2345678901234567|
|00000030| 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 |8901234567890123|
|00000040| 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 |4567890123456789|
|00000050| 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 |0123456789012345|
|00000060| 36 37 38 39                                     |6789            |
+--------+-------------------------------------------------+----------------+

客户端分10此发送的,每次10个字节,但服务端收到的直接100字节,就是黏包

2、半包

服务端

//测试黏包半包服务端
@Slf4j
public class TestNianbaoBanbaoServer {
    public static void main(String[] args) {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();

        try {
            ChannelFuture channelFuture = new ServerBootstrap()
                    .group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    
                    //测试半包,ChannelOption.SO_RCVBUF 滑动窗口大小=8
                    .option(ChannelOption.SO_RCVBUF, 8)
                    
                    
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    channel.pipeline().addLast(new LoggingHandler());
                }
            })
                    .bind(8080).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("server error");
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

客户端

同上

结果

13:18:14.722 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x18f9db8b, L:/192.168.1.91:8080 - R:/192.168.1.91:14236] READ: 32B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 |0123456789012345|
|00000010| 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 |6789012345678901|
+--------+-------------------------------------------------+----------------+

..................

服务端接收到了 3 次循环的完整数据和第四次循环数据中的0 1,第四次的数据就半包了

3、现象分析

粘包

  • 现象,发送 abc def,接收 abcdef
  • 原因
    • 应用层:接收方 ByteBuf 设置太大(Netty 默认 1024)
    • 滑动窗口:假设发送方 256 bytes 表示一个完整报文,但由于接收方处理不及时且窗口大小足够大,这 256 bytes 字节就会缓冲在接收方的滑动窗口中,当滑动窗口中缓冲了多个报文就会粘包
    • Nagle 算法:会造成粘包

半包

  • 现象,发送 abcdef,接收 abc def
  • 原因
    • 应用层:接收方 ByteBuf 小于实际发送数据量
    • 滑动窗口:假设接收方的窗口只剩了 128 bytes,发送方的报文大小是 256 bytes,这时放不下了,只能先发送前 128 bytes,等待 ack 后才能发送剩余部分,这就造成了半包
    • MSS 限制:当发送的数据超过 MSS 限制后,会将数据切分发送,就会造成半包

滑动窗口

  • TCP 以一个段(segment)为单位,每发送一个段就需要进行一次确认应答(ack)处理,但如果这么做,缺点是包的往返时间越长性能就越差

  • 为了解决此问题,引入了窗口概念,窗口大小即决定了无需等待应答而可以继续发送的数据最大值
    • 第一个应答回来之后,窗口向下滑一位

  • 窗口实际就起到一个缓冲区的作用,同时也能起到流量控制的作用
    • 图中深色的部分即要发送的数据,高亮的部分即窗口
    • 窗口内的数据才允许被发送,当应答未到达前,窗口必须停止滑动
    • 如果 1001~2000 这个段的数据 ack 回来了,窗口就可以向前滑动
    • 接收方也会维护一个窗口,只有落在窗口内的数据才能允许接收

标签:Netty,38,32,黏包,30,36,new,半包,31
From: https://www.cnblogs.com/jpymll/p/16821493.html

相关文章

  • Netty进阶-黏包半包解决方案
    4、解决方案4.1、短连接以解决黏包为例。服务端同上,客户端如下//短连接处理黏包,发送后关闭publicclassTestSloveNianbaoClient{publicstaticvoidmain(Strin......
  • Netty进阶-协议
    5.1、Redis协议//redis协议测试publicclassTestRedis{publicstaticvoidmain(String[]args){finalbyte[]line={13,10};NioEventLoop......
  • Netty入门-ButeBuf
    3.5、ByteBuf3.5.1、创建//结果:初始容量256,扩容到512//PooledUnsafeDirectByteBuf(ridx:0,widx:0,cap:256)//PooledUnsafeDirectByteBuf(ridx:0,widx:300,cap......
  • Netty入门-Future & Promise
    3.3、Future&PromiseNetty中的Future与Jdk中Future同名,但是是两个接口,继承关系:Promise---extends-->Future(Netty)-----extend--->Future(JDK)区别:jdkFuture只能......
  • Netty入门-Handler & Pipeline
    3.4、Handler&PipelineChannelHandler用来处理Channel上的各种事件,分为入站、出站两种。所有ChannelHandler被连成一串,就是Pipeline入站处理器通常是ChannelIn......
  • Netty入门-Hello World
    Netty入门1、Netty优势NettyVSNIO,工作量大,bug多需要自己构建协议解决TCP传输问题,如黏包,半包epoll空轮询导致cpu100%对API进行增强,ThreadLocal-->FastThreadLocal......
  • Netty Reactor模型
      1、netty抽象出两个线程池:BossGroup负责监听和建立连接;WorkerGroup负责网络IO的读写2、BossGroup和WorkerGroup类型都是NioEventLoopGroup,相当于一个事件......
  • 【Netty 从成神到升仙系列 大结局】全网一图流死磕解析 Netty 源码
    ......
  • 序列化器---netty
    packagecn.itcast.protocol;importcom.google.gson.*;importjava.io.*;importjava.lang.reflect.Type;importjava.nio.charset.StandardCharsets;/***用......
  • Netty的第一个例子
    importio.netty.bootstrap.ServerBootstrap;importio.netty.channel.ChannelFuture;importio.netty.channel.ChannelInitializer;importio.netty.channel.ChannelOptio......