Netty粘包拆包及使用原理详解

Netty粘包拆包及使用原理详解

在使用Netty进行网络编程时,可能会遇到粘包或拆包的问题,本文将详细讲解Netty粘包拆包的原因和解决方案,并提供两个示例帮助理解。

什么是粘包和拆包

在网络通信中,发送端将多个小的数据包合并成一个大的数据包发送给接收端,称为粘包;接收端在接收数据时,将一个大的数据包拆分成多个小的数据包,称为拆包。由于网络传输是面向字节流的,粘包和拆包是由网络协议栈自动完成的,无法避免。

粘包和拆包产生的原因

粘包的原因

发送端在短时间内快速连续发送多个小的数据包时,这些数据包在经过协议栈处理后被合并成了一个大的数据包发送出去。造成粘包的原因主要有以下两点:

  • 网络传输的数据通常是按照MTU(Maximum Transmission Unit)大小分组的,当发送端发送的数据小于MTU时,会当做一整个数据包发送。
  • 发送端快速发送多个小数据包时,这些数据包很可能会在协议栈中混杂在一起,从而产生粘包的问题。

拆包的原因

拆包问题其实是由Sockets的阻塞读所造成的。在使用read方法时,如果数据没有读完,read方法可能就会阻塞,造成拆包问题。如果连续不断的网络缓冲区中有多个完整包,那就会出现拆包问题。

解决粘包和拆包的方案

分隔符解码器

分隔符解码器的实现比较简单,原理是在传输的数据中增加特殊的分隔符号来区分,当遇到分隔符时就将数据分割成一个完整的包,进行接下来的处理。

该方法需要设计被分割数据包的分隔符,但是在实际中可能会遇到分隔符与数据本身相冲突的情况。例如,常用的分隔符“\n”,如果数据本身含有“\n”,就会导致分割失效。

以下是一个使用分隔符解码器的示例,使用$作为分隔符:

ByteBuf delimiter = Unpooled.copiedBuffer("$".getBytes());
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MyServerHandler());

固定长度解码器

固定长度解码器的实现是指在传输的数据中规定一个固定长度,当数据达到该长度时就将数据分割成一个完整的包进行下一步处理。

该方法的优点是简单易用,但是不适用于不定长数据的处理。

以下是一个使用固定长度解码器的示例,指定数据包长度为10:

pipeline.addLast(new FixedLengthFrameDecoder(10));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MyServerHandler());

示例应用

以下是一个简单的聊天室示例,服务端使用分隔符解码器,客户端使用固定长度解码器:

服务端

public class Server {

    private int port;

    public Server(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes());
                        pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new ServerHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("Server started on port " + port);
            future.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new Server(8080).run();
    }

}

客户端

public class Client {

    private String host;
    private int port;

    public Client(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new FixedLengthFrameDecoder(10));
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new ClientHandler());
                    }
                });

            ChannelFuture future = bootstrap.connect(host, port).sync();
            System.out.println("Client connected to " + host + ":" + port);

            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String message = in.readLine();
                if (message == null) {
                    break;
                }
                future.channel().writeAndFlush(message + "\n");
            }

            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new Client("localhost", 8080).run();
    }

}

完整的示例代码可以在我的GitHub仓库中查看:https://github.com/crazymakercircle/Netty-Demo。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Netty粘包拆包及使用原理详解 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Apache Log4j2 报核弹级漏洞快速修复方法

    下面我来为您讲解“Apache Log4j2报核弹级漏洞快速修复方法”的完整攻略。 一、背景介绍 在2021年12月8日,美国网络安全局 (NSA) 警告公众一种名为 Log4Shell 的漏洞,该漏洞存在于 Log4j 2.x 中,攻击者可通过该漏洞远程执行代码,甚至可以获得系统控制权。由于该漏洞的严重性,被称为“核弹级漏洞”。 二、修复方法 1.更新 L…

    Java 2023年6月2日
    00
  • 什么是Java垃圾回收器?

    Java垃圾回收器是Java虚拟机(JVM)中的一项机制,用于在程序运行过程中动态地回收不再使用的对象所占据的内存空间,以避免内存泄露及程序运行时出现OutOfMemoryError等内存相关错误。 Java垃圾回收器的主要功能是自动回收堆中的垃圾对象,堆是Java程序中被存储对象的区域。Java垃圾回收器的工作过程一般包括标记、清除、压缩和复制等步骤。 其…

    Java 2023年5月11日
    00
  • 简单学习Java抽象类要点及实例

    下面是关于“简单学习Java抽象类要点及实例”的完整攻略。 什么是抽象类 抽象类是一种特殊的类,它不能实例化,只能被继承。抽象类中可以有抽象方法(没有方法体),也可以有非抽象方法(有方法体),但是抽象类中至少要有一个抽象方法。抽象类的主要作用是为了让子类继承并实现它的抽象方法,以此来完成对某个行为的规范和约束。 抽象类的语法 抽象类的语法格式如下: publ…

    Java 2023年5月31日
    00
  • java实现简单的扫雷小游戏

    讲解”Java实现简单的扫雷小游戏”的攻略,以下是具体步骤: 第一步:界面设计 扫雷游戏主要分为三个步骤:游戏开始、游戏进行中、游戏结束。我们需要根据这些状态设计出对应的UI界面,具体需要设计的内容包括: 开始界面:包括游戏标题、游戏难度选择、开始游戏按钮。 进行中界面:包括剩余雷数、当前用时、扫雷主界面、游戏菜单等。 结束界面:包括胜利或失败的提示、重新开…

    Java 2023年5月19日
    00
  • 基于重定向RedirectAttributes的用法解析

    基于重定向 RedirectAttributes 的用法解析 在 Spring MVC 中,经常会使用重定向来实现一些跳转的功能。而 RedirectAttributes 则是在使用重定向时用于向跳转页面传递数据的对象。 RedirectAttributes 的用法 使用 RedirectAttributes 一般需要按以下步骤进行: 在处理请求的方法中通过…

    Java 2023年6月15日
    00
  • 详解SpringBoot实现fastdfs防盗链功能的示例代码

    以下是“详解SpringBoot实现fastdfs防盗链功能的示例代码”的完整攻略: 防盗链功能概念 防盗链技术可以防止其他站点盗链本站的内容,从而保证网站安全及资源不被滥用。在FastDFS中,通过配置nginx.conf文件实现防盗链。 安装配置FastDFS 首先,需要在本地或服务器上安装并配置FastDFS。可以参考FastDFS官网及论坛的相关文档…

    Java 2023年5月20日
    00
  • Java Spring MVC 上传下载文件配置及controller方法详解

    下面是关于“Java Spring MVC 上传下载文件配置及controller方法详解”的完整攻略,包含两个示例说明。 Java Spring MVC 上传下载文件配置及controller方法详解 在Java Spring MVC中,文件上传和下载是常见的功能。本文将介绍如何配置文件上传和下载,并提供两个示例说明。 步骤一:配置文件上传 首先,我们需要…

    Java 2023年5月17日
    00
  • 基于IDEA创建SpringMVC项目流程图解

    下面是基于IDEA创建SpringMVC项目的完整攻略流程图解: 前置条件 安装JDK和IDEA 熟悉Java和SpringMVC开发 创建SpringMVC项目 启动IDEA,点击“Create New Project”来创建新项目 选择“Spring Initializr”来创建SpringMVC项目 在“New Project”窗口中,选择“Sprin…

    Java 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部