Netty进阶——粘包与半包(代码示例)

news2024/10/5 13:14:11

目录

    • 一、消息粘包和消息半包的概述
      • 1.1、消息粘包
      • 1.2、消息半包
    • 二、粘包现象代码示例
      • 2.1、粘包现象服务端示例代码
      • 2.2、粘包现象客户端示例代码
      • 2.3、分别启动服务端,客户端,查看服务端结果输出
    • 三、半包现象代码示例
      • 3.1、半包现象服务端示例代码
      • 3.2、半包现象客户端示例代码
      • 3.3、分别启动服务端,客户端,查看服务端结果输出

一、消息粘包和消息半包的概述

1.1、消息粘包

  • 当缓冲区足够大,由于网络不稳定种种原因,可能会有多条消息从通道读入缓冲区,此时如果无法分清数据包之间的界限,就会导致粘包问题。

1.2、消息半包

  • 若消息没有接收完,缓冲区就被填满了,会导致从缓冲区取出的消息不完整,即半包的现象。

二、粘包现象代码示例

2.1、粘包现象服务端示例代码

  • 服务端代码

    package com.example.nettytest.netty.day5;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.logging.LogLevel;
    import io.netty.handler.logging.LoggingHandler;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @description:   Netty粘包现象演示服务端
     *                 消息粘包:当缓冲区足够大,由于网络不稳定种种原因,可能会有多条消息从通道读入缓冲区,
     *                         此时如果无法分清数据包之间的界限,就会导致粘包问题;
     * @author: xz
     */
    @Slf4j
    public class NettyServerTest {
    
        public static void main(String[] args) {
            new NettyServerTest().start();
        }
    
        void start() {
            NioEventLoopGroup boss = new NioEventLoopGroup(1);
            NioEventLoopGroup worker = new NioEventLoopGroup();
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap()
                    .channel(NioServerSocketChannel.class)
                    .group(boss, worker)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                //会在连接channel建立成功后,触发active事件
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    log.debug("connected>>>>>>>>>>>>>>>> {}", ctx.channel());
                                    super.channelActive(ctx);
                                }
                                @Override
                                public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                                    log.debug("disconnect>>>>>>>>>>>>>>>> {}", ctx.channel());
                                    super.channelInactive(ctx);
                                }
                            });
                        }
                    });
                ChannelFuture channelFuture = serverBootstrap.bind(8080);
                log.debug("{}>>>>>>>>>>>>>>>> binding...", channelFuture.channel());
                channelFuture.sync();
                log.debug("{}>>>>>>>>>>>>>>>> bound...", channelFuture.channel());
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                log.error("server error", e);
            } finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
                log.debug(">>>>>>>>>>>>>>>>stoped");
            }
        }
    }
    

2.2、粘包现象客户端示例代码

  • 客户端代码示例

    package com.example.nettytest.netty.day5;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import lombok.extern.slf4j.Slf4j;
    /**
     * @description: Netty粘包现象演示客户端
     * @author: xz
     */
    @Slf4j
    public class NettyClientTest {
    
        public static void main(String[] args) {
            new NettyClientTest().start();
        }
    
        void start() {
            NioEventLoopGroup worker = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap()
                    .channel(NioSocketChannel.class)
                    .group(worker)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            log.debug("connetted》》》》》》》》》》》》》》》");
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                //会在连接channel建立成功后,触发active事件
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    log.debug("遍历 sending 每次发送16个字节》》》》》》》》》》》》》》》");
                                    for (int i = 0; i < 10; i++) {
                                        //设置缓冲区大小16个字节
                                        ByteBuf buffer = ctx.alloc().buffer(16);
                                        buffer.writeBytes(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
                                        ctx.writeAndFlush(buffer);
                                    }
                                }
                            });
                        }
                    });
                ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                log.error("client error", e);
            } finally {
                worker.shutdownGracefully();
            }
        }
    }
    

2.3、分别启动服务端,客户端,查看服务端结果输出

  • 先启动服务端
    在这里插入图片描述
  • 再启动客户端
    在这里插入图片描述
  • 再次查看服务端
    注:可下图输出结果可知:服务器端的某次输出,可以看到一次就接收了 160 个字节,而非分 10 次接收。在这里插入图片描述

三、半包现象代码示例

3.1、半包现象服务端示例代码

  • 为现象明显,服务端添加一下接收缓冲区,其它代码不变

    serverBootstrap.option(ChannelOption.SO_RCVBUF, 10);
    
  • 服务端完整示例代码

    package com.example.nettytest.netty.day5;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.logging.LogLevel;
    import io.netty.handler.logging.LoggingHandler;
    import lombok.extern.slf4j.Slf4j;
    /**
     * @description:   Netty半包现象演示服务端
     *                 消息半包:若消息没有接收完,缓冲区就被填满了,会导致从缓冲区取出的消息不完整,即半包的现象。
     * @author: xz
     */
    @Slf4j
    public class NettyServerTest1 {
        public static void main(String[] args) {
            new NettyServerTest().start();
        }
    
        void start() {
            NioEventLoopGroup boss = new NioEventLoopGroup(1);
            NioEventLoopGroup worker = new NioEventLoopGroup();
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap()
                        .channel(NioServerSocketChannel.class)
                        //服务端添加接收缓冲区,大小设置10
                        .option(ChannelOption.SO_RCVBUF, 10)
                        .group(boss, worker)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                                ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        log.debug("connected================== {}", ctx.channel());
                                        super.channelActive(ctx);
                                    }
                                    @Override
                                    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                                        log.debug("disconnect================== {}", ctx.channel());
                                        super.channelInactive(ctx);
                                    }
                                });
                            }
                        });
                ChannelFuture channelFuture = serverBootstrap.bind(8080);
                log.debug("{} binding====================", channelFuture.channel());
                channelFuture.sync();
                log.debug("{} bound====================", channelFuture.channel());
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                log.error("server error", e);
            } finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
                log.debug("stoped======================");
            }
        }
    }
    

3.2、半包现象客户端示例代码

  • 客户端代码示例无变化,完整代码如下

    package com.example.nettytest.netty.day5;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import lombok.extern.slf4j.Slf4j;
    /**
     * @description: Netty半包现象演示客户端
     * @author: xz
     */
    @Slf4j
    public class NettyClientTest1 {
        public static void main(String[] args) {
            new NettyClientTest1().start();
        }
    
        void start() {
            NioEventLoopGroup worker = new NioEventLoopGroup();
            try {
                Bootstrap bootstrap = new Bootstrap()
                    .channel(NioSocketChannel.class)
                    .group(worker)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            log.debug("connetted----------------");
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                //会在连接channel建立成功后,触发active事件
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    log.debug("sending----------------");
                                    for (int i = 0; i < 10; i++) {
                                        ByteBuf buffer = ctx.alloc().buffer(16);
                                        buffer.writeBytes(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
                                        ctx.writeAndFlush(buffer);
                                    }
                                }
                            });
                        }
                    });
                ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                log.error("client error", e);
            } finally {
                worker.shutdownGracefully();
            }
        }
    }
    

3.3、分别启动服务端,客户端,查看服务端结果输出

  • 先启动服务端
    在这里插入图片描述
  • 再启动客户端
    在这里插入图片描述
  • 再次查看服务端
    注:可下图输出结果可知:服务器端的某次输出,可以看到接收的消息被分为两节,第一次 16 字节,第二次 32 字节,第三次 64 字节,第四次 48 字节
    在这里插入图片描述
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/47490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaSE】学习异常

前言&#xff1a; 作者简介&#xff1a;爱吃大白菜1132 人生格言:纸上得来终觉浅&#xff0c;绝知此事要躬行 如果文章知识点有错误的地方不吝赐教&#xff0c;和大家一起学习&#xff0c;一起进步&#xff01; 如果觉得博主文章还不错的话&#xff0c;希望三连支持&#xff01…

D-020 SPI FLASH硬件电路设计

SPI FLASH硬件电路设计1 简介2 EEPROM 和SPI Flash的区别3 电路设计实战4 电路设计要点1 简介 SPI FLASH(Serial Peripheral Interface)是串行外设接口的缩写&#xff0c;是一种高度、全双工、同步的通信总线。一般应用在MCU与外围设备之间通讯&#xff0c;广泛应用在FLASH&am…

从模型容量的视角看监督学习

这几天看离线强化学习瞎想的&#xff0c;不一定正确&#xff0c;仅记录个人想法 文章目录1. 监督学习的本质2. 容量视角下的模型选择、正则化和归纳偏置3. 几点启发1. 监督学习的本质 我认为监督学习的本质在于在过拟合和欠拟合之间取得平衡&#xff0c;捋一下逻辑 我们知道&a…

基于JAVA+SpringMVC+Mybatis+Vue+MYSQL的医药销售管理系统

项目介绍 药品一直以来在人类生活中扮演着非常重要的角色&#xff0c;随着时代的飞速发展&#xff0c;人们基本已经告别了那个缺医少药的年代&#xff0c;各大药房基本随处都可以购买&#xff0c;但是很多时候因为没有时间或者在药店很难找到自己想要购买的药品&#xff0c;所…

[附源码]计算机毕业设计springboot个人博客系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Windows远程连接centos7图形化界面,安装xrdp

Windows远程连接centos7图形化界面&#xff0c;安装xrdp写在最前面准备工作查看ubuntu系统的版本信息和gcc版本尝试进入图形化界面更新yum下载安装图形化界面查询本地是否有Server with GUI group安装Server with GUI设置系统启动默认加入GUI界面安装epel库&#xff08;epel是社…

Djiango 模版系统详解(ORM数据模型-使用mysql数据库增删改查)

djiango模版系统&#xff1a; 用于自动渲染一个文本文件&#xff0c;一般用于HTML页面&#xff0c;模版引擎渲染的最终HTML内容返回给客户端浏览器 模版系统分成两部分 静态部分&#xff1a; 例如html css .js 动态部分 djiango 模版语言&#xff0c;类似于jinja语法变量定义&…

ArcGIS QGIS学习二:图层如何只显示需要的部分几何面数据(附最新坐标边界下载全国省市区县乡镇)

文章目录前言准备SHP数据ArcMap 的筛选QGIS 的筛选如何编写查询条件前言 当我们用GIS软件打开一个SHP文件的时候&#xff0c;会显示出里面全部的几何图形&#xff0c;假如我只想要其中的一部分数据显示出来&#xff0c;其他的均不要显示&#xff0c;有那么几种操作方法。 我们…

UE4使用蓝图实现角色冲刺

又学了几天&#xff0c;前面记录了如何使用蓝图实现开关门&#xff0c;这次来实现一下角色的冲刺、瞬移的操作 一般玩游戏的时候&#xff0c;可能都会有按左shift键让角色从行走变成奔跑的状态&#xff0c;又或者双击回避键角色瞬移躲避等操作 那就先实现一下加速奔跑吧&…

Python毕业设计必备案例:【学生信息管理系统】

嗨害大家好鸭&#xff01;我是小熊猫~ 最近看大家好像都快放假啦~ 但是放假之前有的小朋友要做毕业设计~ 有的要期末考试~ 那么今天来给大家整一个小的毕业管理系统案例 康康整题思路是怎么样的~ 源码、资料电子书点击这里获取 功能模块 基本信息管理和学生成绩管理。 基…

【自用】VUE 获取登录用户名 显示在其他页面上

大步骤一、准备工作 步骤1&#xff1a; 安装 js-cookie 依赖 npm install js-cookie --save步骤2&#xff1a; 在登录页面中引入 js-cookie 依赖 <script> import jsCookie from js-cookie; </script>大步骤二、在 登录页面 的vue文件 中使用它&#xff01; …

[附源码]计算机毕业设计springboot基于JAVA技术的旅游信息交互系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计SSM泸定中学宿舍管理系统设计(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

python零基础入门(完整版)

1python软件下载 我们需要下载python语言翻译器&#xff08;也就是运行环境&#xff09;&#xff0c;和一个用来写python的软件&#xff0c;用它写python语言比较方便 首先打开百度&#xff0c;下载org 然后点击下载 然后选择一个稳定版本下载 然后会跳转到一个页面&#xff0c…

kubernetes-service详解

kubernetes-service详解 文章目录kubernetes-service详解一、Service操作一&#xff1a;创建集群内部可访问的Service操作二&#xff1a;创建集群外部也可访问的Service二、pod详解pod的资源清单pod 配置基本配置镜像拉取启动命令环境变量端口配置资源配额一、Service 虽然每个…

NumPy 数组应用初探

NumPy是Python中科学计算的基本包。它是一个Python库&#xff0c;提供了一个多维数组对象&#xff0c;各种派生对象&#xff08;如屏蔽数组和矩阵&#xff09;&#xff0c;以及对数组进行快速操作的各种例程&#xff0c;包括数学、逻辑、形状操作、排序、选择、I/O、离散傅立叶…

Python-可视化单词统计词频统计中文分词

可视化单词统计词频统计中文分词项目架构新建文件单词计数全文单词索引中文分词统计词频源代码项目架构 新建一个文件&#xff0c;输入文件的内容&#xff0c;查询此文件中关键字的出现的次数&#xff0c;关键字出现的位置&#xff0c;将所有的文本按照中文分词的词库进行切割…

运筹说 第76期 | 最短路问题

通过前面的学习&#xff0c;我们已经学会了图与网络问题中图的基本概念和最小树问题&#xff0c;本期小编带大家学习最短路问题。 一 最短路问题 最短路问题是网络理论中应用最广泛的问题之一。许多优化问题可以使用这个模型&#xff0c;如设备更新、管道敷设、线路安排、厂区…

tensorboard attempted to bind to port 6006,but it was already in use

我尝试运行tensorboard是遇到错误 这篇博客给了两个解决方案&#xff1a;

弹出 think-cell 许可证密钥窗口丨使用教程

think-cell需要使用有效的许可证密钥才能运行。每次启动 Microsoft PowerPoint 或 Microsoft Excel 时&#xff0c;都会检查许可证密钥。若您的系统上找不到任何有效的许可证信息&#xff0c;或许可证密钥即将到期&#xff0c;则会显示 think-cell 许可证密钥对话窗口。 thin…