• TCP 粘包和拆包


    1.基本介绍

    1. TCP是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端就难于分辨出完整的数据包了,因为面向流的通信是无消息保护边界的

    2. 由于TCP无消息保护边界, 需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题

     

    2.TCP粘包、拆包图解

     3. TCP 粘包和拆包解决方案

    1. 使用自定义协议 + 编解码器 来解决

    2. 关键就是要解决 服务器端每次读取数据长度的问题, 这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免的TCP 粘包、拆包 。

     

    4.解决详情代码

    1. 服务端

    public class MyServer {
        public static void main(String[] args) throws InterruptedException {
            NioEventLoopGroup boss = new NioEventLoopGroup();
            NioEventLoopGroup worker = new NioEventLoopGroup();
            try{
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(boss,worker)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ServerInitalizer());
                ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
                //监视关闭
                channelFuture.channel().closeFuture().sync();
    
            }finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
            }
        }
    }

    2.服务端初始化类

    //channel初始化类
    public class ServerInitalizer extends ChannelInitializer<SocketChannel> {
        /**
         */
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            //需要添加处理类
            pipeline.addLast(new ServerHandler());
    
        }
    }

    3.服务端channel处理类

    //服务端channel处理类
    public class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
        //统计服务器接收数据次数,展示粘包效果
        private int num = 0;
    
        /**
         * 接收客户端消息
         * 模拟粘包拆包,可能一次读取完成,也有可能多次读取
         */
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            //创建msg数组
            byte[] bytes = new byte[msg.readableBytes()];
            //msg读取到数组
            msg.readBytes(bytes);
            //转为字符串
            String s = new String(bytes, Charset.forName("utf-8"));
            System.out.println("服务器第" + (++this.num) + "次接收数据为:" + s);
            //回送消息给客户端
            ByteBuf byteBuf = Unpooled.copiedBuffer("服务端回送消息....", Charset.forName("utf-8"));
            ctx.writeAndFlush(byteBuf);
        }
    }

    4.客户端

    public class MyClient {
        public static void main(String[] args) throws InterruptedException {
            NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
            try{
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(eventExecutors)
                        .channel(NioSocketChannel.class)
                        .handler(new ClientInitalizer());
                //建立链接
                ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7000).sync();
                //监视关闭
                channelFuture.channel().closeFuture().sync();
            }finally {
                eventExecutors.shutdownGracefully();
            }
        }
    }

    5.客户端初始化类

    public class ClientInitalizer extends ChannelInitializer<SocketChannel> {
    
    
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
    
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new ClientHandler());
        }
    }

    6.客户端channel处理类

    public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf>{
        //统计服务器接收数据次数,展示粘包效果
        private int num=0;
        /**
         *客户端接收消息
         */
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            //创建msg数组
            byte[] buffer = new byte[msg.readableBytes()];
            msg.readBytes(buffer);
            //转为字符串
            String s = new String(buffer, Charset.forName("utf-8"));
            System.out.println("服务端发来消息:"+s+",消息次数:"+(++this.num));
    
        }
    
        /**
         *客户端发送消息
         * @param ctx
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            /*
            模拟粘包拆包,可能一次发送完成,也有可能多次发送
             */
            for (int i = 0; i <10 ; ++i) {
                //模拟粘包
                ByteBuf buffer = Unpooled.copiedBuffer("hello,server " + i, Charset.forName("utf-8"));
                ctx.writeAndFlush(buffer);
            };
        }
    }

     

     

     

  • 相关阅读:
    【iOS】找工作的面试题集锦
    APP项目优化--启动速度优化篇
    【Swift】Timer定时器到底准不准确?
    leetcode刷题 495~
    leetcode刷题 464~
    leetcode刷题 441~
    leetcode刷题 420~
    leetcode刷题 396~
    leetcode刷题 373~
    leetcode刷题 307~
  • 原文地址:https://www.cnblogs.com/hyy9527/p/13100354.html
Copyright © 2020-2023  润新知