粘包和拆包问题解决方案汇总

上一篇 << 下一篇 >>>序列化与反序列化知识点汇总


1.解码器LineBaseDFrameDecoder以固定的长度发送数据,缓冲区以固定长度读取-----不大现实,该编辑器无Encoder

socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(10)); //参数为一次接受的数据长度

2.解码器LineBaseDFrameDecoder自动解析,只要在发送的时候以 (\n或者\r\n)作为边界,服务端自动会解析出来。----该编辑器无Encoder

服务端代码:
serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                // 设置我们分割最大长度为1024
                socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                // 获取数据的结果为string类型,客户端加密,服务端解密
                socketChannel.pipeline().addLast(new StringEncoder());
                socketChannel.pipeline().addLast(new ServerHandler());
            }
        });
客户端代码:
ctx.writeAndFlush(Unpooled.copiedBuffer("平均突破3万月薪\n", CharsetUtil.UTF_8));

3.利用编码器DelimiterBasedFrameDecoder自动解析,可以自定义边界标识符,LineBaseDFrameDecoder只是他的一种特例。

服务端代码:
serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                ByteBuf byteBuf = Unpooled.copiedBuffer("\t".getBytes());
                socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));
                // 获取数据的结果为string类型,客户端加密,服务端解密
                socketChannel.pipeline().addLast(new StringEncoder());
                socketChannel.pipeline().addLast(new ServerHandler());
            }
        });
客户端代码:
ctx.writeAndFlush(Unpooled.copiedBuffer("我是测试代码\t", CharsetUtil.UTF_8));

4.通过在消息头定义长度字段来标识消息总长度。
nginx的启动和这个一样有两个进程,也是一个master接收请求,一个work处理请求。

// 服务端代码
public class NettyServer {
    private static int port = 9090;

    public static void main(String[] args) {
        /**
         * Channel——Socket,降低了直接使用Socket的复杂度
         * EventLoop——控制流、多线程处理、并发
         *       一个EventLoopGroup包含一个或者多个EventLoop;
         *       一个EventLoop在它的生命周期内只和一个Thread绑定
         *       一个Channel在它的生命周期内只注册一个EventLoop;
         *       一个EventLoop可能会被分配给一个或多个Channel。
         * ChannelFuture——异步通知
         *       其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。
         */

        /**boss线程组:用于接收客户端连接的请求*/
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        /**work线程组:用于处理客户端连接的读写操作*/
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        /**创建ServerBootstrap*/
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        /**设置分组、channel管道及Handler处理器*/
        serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        /**向pipeline中添加编码、解码、业务处理的handler*/
                        /**定义消息分割器,解决粘包、拆包问题,设置我们分割最大长度为1024,发送消息的时候必须以\n结尾才可以识别*/
                        socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                        socketChannel.pipeline().addLast(new StringDecoder());
                        /**处理自定义处理器*/
                        socketChannel.pipeline().addLast(new ServerHandler());
                    }
                });

        try {
            /**绑定端口,开启监听*/
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("服务器启动成功:" + port);
            /**
             * closeFuture()是开启了一个channel的监听器,负责监听channel是否关闭的状态,如果未来监听到channel关闭了,子线程才会释放
             * sync() 让主线程同步等待子线程结果
             */
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            /**优雅的关闭连接*/
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }

}
//客户端代码(和服务端相比就bootstrap和channel定义不一样)
public class NettyClient {
    public static void main(String[] args) {
        //创建nioEventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress("127.0.0.1", 9090))
                .handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(new ClientHandler());
                    }
                });
        try {
            // 发起同步连接
            ChannelFuture sync = bootstrap.connect().sync();
            sync.channel().closeFuture().sync();
        } catch (Exception e) {

        } finally {
            group.shutdownGracefully();
        }

    }
}
//消息的发送与接收
public class ClientHandler extends SimpleChannelInboundHandler {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        /**
         * 发送时必须设置标记边界\n或\r\n ,否则会存在粘包拆包的情况
         */
        for(int i=0;i<10;i++) {
            // 发送数据
            ctx.writeAndFlush(Unpooled.copiedBuffer("每特教育平均月薪突破多少?\n", CharsetUtil.UTF_8));
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        String result = msg.toString(CharsetUtil.UTF_8);
        /**
         * 方式一:启动时设置LineBasedFrameDecoder,则直接读取数据,不存在粘包拆包问题
         */
        System.out.println("resp:"+result);
        /**
         * 方式二:手动根据边界写代码
         */
        String[] sp = result.split("\n");
        for(String s:sp) {
            System.out.println("resp:" + s);
        }
        /**
         * 可以在读取的时候直接响应结果
         * a、格式得ByteBuf,否则会出现乱码问题
         * b、响应也属于发送,后面也得加\n或\r\n
         */
        ctx.writeAndFlush(Unpooled.copiedBuffer("结果响应\n", CharsetUtil.UTF_8));
    }
}

推荐阅读:
<< << << << << << << << << << << << <<<序列化与反序列化知识点汇总
<< <<

你可能感兴趣的:(粘包和拆包问题解决方案汇总)