【四】Netty 分隔符和定长解码器的应用

news2024/12/26 21:40:48

Netty 分隔符和定长解码器的应用

  • 理论说明
  • LineBasedFrameDecoder 开发
    • 大概流程
    • 代码展示
      • netty 依赖
      • EchoServer 服务端启动类
      • EchoServerHandler
      • EchoClient
      • EchoClientHandler
    • 结果打印
      • 客户端打印
      • 服务端打印
  • FixedLengthFrameDecoder 开发
    • 代码展示
      • EchoServer 服务端启动类
      • EchoFixServerHandler 业务处理类
      • 客户端
    • 效果展示
      • 服务端打印
  • 总结

理论说明

TCP 以流的方式进行数据传输,长层的应用协议为了对消息进行区分,往往采用如下四种方式
(1)消息定长固定,累计读取到长度总和为定长LEN的报文后,就认为读取到了一个完整的消息:将计数器置位,重新开始读取下一个数据报
(2)将回车换行符作为消息结束符,例如FTP协议,这种方式在文本协议中应用比较广泛。
(3)将特殊的分隔符作为消息的结束标志,回车换行符就是一种特殊的结束标志。
(4)通过在消息头中定义长度字段来标识消息的总长度
Netty对上述四种应用都做了统一的抽象,提供了四种解码器来解决对应的问题,使用起来非常方便。有了这些解码器,用户不需要自己对读取的报文进行人工解码,也不需要考虑粘包和拆包的问题。
【三】Netty 解决粘包和拆包问题 我们介绍了利用LineBasedFrameDecoder 解决了TCP的粘包问题。本节我们继续学习下另外两种实用的解码器 DelimiterBasedFrameDecoderFixedLengthFrameDecoder,前者可以自动完成以分隔符做结束标志的消息的解码,后者可以自动完成对定长消息的解码,它们都能解决TCP粘包和拆包导致的读半包问题。

LineBasedFrameDecoder 开发

大概流程

在这里插入图片描述
EchoServer接受到EchoClient的请求消息后,将其打印出来,然后将原始消息返回给客户端,消息以 $_ 作为分隔符.

代码展示

netty 依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>5.0.0.Alpha1</version>
        </dependency>

EchoServer 服务端启动类

public class EchoServer {

    public static void main(String[] args) {
        new EchoServer().bind(8080);
    }

    public void bind(int port) {
        //配置服务端的NIO线程组
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel)
                                throws Exception {
                            ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
                            //添加 DelimiterBasedFrameDecoder 解码器,入参是对应的特殊字符 $_,作为分隔符
                            //1024 是单条消息的最大长度,当达到最大 长度也没有分隔符时就抛出异常
                            // 我们可以根据我们自己的需要,选择对应的特殊字符
                            socketChannel.pipeline().
                                    addLast(new
                                            DelimiterBasedFrameDecoder(
                                            1024, delimiter));
                            socketChannel.pipeline()
                                    .addLast(new StringDecoder());
                           /* socketChannel.pipeline()
                                    .addLast(new StringEncoder());*/
                            socketChannel.pipeline().addLast(
                                    new EchoServerHandler()
                            );
                        }
                    });
            //绑定端口,等待同步成功
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("server is started");
            //等待服务器监听端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
             //优雅退出
            parentGroup.shutdownGracefully();
            childGroup.shutdownGracefully();
        }
    }
}

EchoServerHandler

public class EchoServerHandler extends ChannelHandlerAdapter {
    int counter=0;
    public void channelRead(ChannelHandlerContext context,Object body){
        //String body=(String)msg;
        // EchoServer 在 添加 childHandler中,添加了三个 ChannelHandler
        // 1.DelimiterBasedFrameDecoder 自动对请求消息进行解码,
        //    后续的ChannelHandler接受到的body就是完整的消息包
        // 2.StringDecoder 它将ByteBuf 解码成字符串对象
        // 3.EchoServerHandler 接受到的 body 对象就是解码后的字符串对象
        System.out.println("This is "+ ++counter+ " times receive client : 【"+body+"】");
        body+="$_";
        //由于我们设置  DelimiterBasedFrameDecoder 过滤掉了分隔符,所以,返回客户端时
        //需要将分隔符$_ 拼接上去,最后创建ByteBuf对象返回给客户端。
        ByteBuf echo= Unpooled.copiedBuffer(body.toString().getBytes());
        context.writeAndFlush(echo);

    }
}

EchoClient

public class EchoClient {
    public static void main(String[] args) {
        new EchoClient().connect("127.0.0.1",8080);
    }

    public void connect(String host,int port){
        //配置客户端NIO线程组
        EventLoopGroup loopGroup=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(loopGroup)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ByteBuf delimiter= Unpooled.copiedBuffer("$_".getBytes());
                            //添加 DelimiterBasedFrameDecoder 解码器,入参是对应的特殊字符 $_作为分隔符
                            //1024 是单条消息的最大长度,当达到最大 长度也没有分隔符时就抛出异常
                            // 我们可以根据我们自己的需要,选择对应的特殊字符
                            socketChannel.pipeline()
                                    .addLast(new DelimiterBasedFrameDecoder(
                                            1024,delimiter));
                            socketChannel.pipeline()
                                    .addLast(new StringDecoder());
                           /* socketChannel.pipeline()
                                    .addLast(new StringEncoder());*/
                            socketChannel.pipeline()
                                    .addLast(new EchoClientHandler());
                        }
                    });
            //进行tcp 链接服务端
            ChannelFuture future=bootstrap.connect(host,port).sync();
            System.out.println("client is started ....");
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //优雅退出线程组
            loopGroup.shutdownGracefully();
         }
    }
}

EchoClientHandler

public class EchoClientHandler extends ChannelHandlerAdapter {
    private int counter;
    static final String ECHO_REQ = "Hi ,echo ,welcome to Netty $_";

    public void channelActive(ChannelHandlerContext context) {
        for (int i = 0; i < 10; i++) {
            context.writeAndFlush(
                    Unpooled.copiedBuffer(ECHO_REQ.getBytes())
            );
        }
    }

    public void channelRead(ChannelHandlerContext context,Object obj){
        System.out.println("the counter is "+ ++counter+" times receive server 【"+obj+" 】");
    }
}

结果打印

客户端打印

在这里插入图片描述

服务端打印

在这里插入图片描述

FixedLengthFrameDecoder 开发

FixedLengthFrameDecoder 是固定长度解码器,他能够按照指定的长度对消息进行自动解码,开发者不需要考虑TCP 粘包/拆包问题。
开发的代码比较简单,我们展示下代码:

代码展示

EchoServer 服务端启动类

public class EchoServer {

    public static void main(String[] args) {
        new EchoServer().bind(8080);
    }

    public void bind(int port) {
        //配置服务端的NIO线程组
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel)
                                throws Exception {
                            //定长解码器
                            socketChannel.pipeline().
                                    addLast(new
                                            FixedLengthFrameDecoder(
                                            20));
                            socketChannel.pipeline()
                                    .addLast(new StringDecoder());
                           /* socketChannel.pipeline()
                                    .addLast(new StringEncoder());*/
                            socketChannel.pipeline().addLast(
                                    new EchoFixServerHandler()
                            );
                        }
                    });
            //绑定端口,等待同步成功
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("server is started");
            //等待服务器监听端口关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
			//优雅退出
            parentGroup.shutdownGracefully();
            childGroup.shutdownGracefully();
        }
    }
}

EchoFixServerHandler 业务处理类

public class EchoFixServerHandler extends ChannelHandlerAdapter {
    public void channelRead(ChannelHandlerContext context,Object msg){
        System.out.println("receive client message is 【"+msg+"】");
    }
}

客户端

我们采用NetAssist 网络助手来进行测试。
下载地址:https://download.csdn.net/download/echohuangshihuxue/87311946?spm=1001.2014.3001.5503
在这里插入图片描述

效果展示

服务端打印

在这里插入图片描述
由于我们配置的是定长20个字节,所以发送的内容分为三次来接受打印了。

总结

DelimiterBasedFrameDecoder 解码器用于对使用分隔符结尾的消息进行自动解码,
FixedLengthFrameDecoder 用于对固定长度的消息进行自动解码,有了上述两种解码器,再结合其他解码器,如字符串解码器,就可以轻松的完成对很多消息的自动解码,而且不再考虑TCP粘包和拆包导致的读半包的问题,极大的提升了开发效率。

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

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

相关文章

【云原生】k8s之pod控制器

内容预知 前言 1.pod控制器的相关知识 1.1 pod控制器的作用 1.2 pod控制器的多种类型 1.3 pod容器中的有状态和无状态的对比 &#xff08;1&#xff09;有状态实例 &#xff08;2&#xff09;无状态实例 2.Deployment控制器 2.1 SatefulSet 控制器的运用 2.1 Sateful…

从0到1完成一个Vue后台管理项目(六、404页)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

[LeetCode周赛复盘] 第 95 场周赛20230107

[LeetCode周赛复盘] 第 95 场周赛20230107 一、本周周赛总结二、 [Easy] 2525. 根据规则将箱子分类1. 题目描述2. 思路分析3. 代码实现三、[Medium] 2526. 找到数据流中的连续整数![在这里插入图片描述](https://img-blog.csdnimg.cn/237210adb20e457aaf2671e6e8f9e43b.png)2. …

Linux系统中C++多态和数据封装的基本方法

大家好&#xff0c;今天主要和大家分享一下&#xff0c;多态&#xff0c;数据封装的使用方法。 目录 第一&#xff1a;C中的多态 第二&#xff1a;C中数据封装方法 第一&#xff1a;C中的多态 C多态意味着调用成员函数时&#xff0c;会根据调用函数的对象的类型来执行不同的函…

将内核加载到内存

文章目录前言前置知识代码实验操作前言 本博客记录《操作系统真象还原》第五章第3个实验的操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a;将内核载入内存,初始化内核代码 实验原理 编写内核程序。将内核程序用dd命令复制到…

Odoo 16 企业版手册 - 库存管理之存储类别

存储类别 Odoo中的存储类别功能将允许您将许多存储位置分组到一个类别下。您可以在Odoo 库存管理模块中创建许多此类类别&#xff0c;这将有助于执行更智能的放置操作。在配置存储类别之前&#xff0c;您必须配置库存中可用的存储位置。然后&#xff0c;您可以将它们分组到一个…

LeetCode刷题模版:31 - 40

目录 简介31. 下一个排列32. 最长有效括号33. 搜索旋转排序数组34. 在排序数组中查找元素的第一个和最后一个位置35. 搜索插入位置36. 有效的数独37. 解数独38. 外观数列39. 组合总和40. 组合总和 II结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指…

电影解说开头怎么写吸引人?

电影解说开头怎么写吸引人&#xff1f;很多电影解说创作者文采不够好&#xff0c;开头不知道怎么写&#xff1f;毕竟想留住用户继续观看视频&#xff0c;开头是至关重要的&#xff0c;今天笔者就分享电影解说文案万能公式模板&#xff0c;让大家创作更简单&#xff01;一个好的…

feature engnineering 特征工程

特征工程数值型变量standardizationlog_transformation(使其符合正态分布)polynomial features分类型变量orinigalencoderonehot encoder分类创造下的数值以下代码根据Abhishek Thakur在kaggle上的机器学习30天 &#xff08;b站&#xff09; (kaggle)可惜的是&#xff0c;我没有…

Oracle 19c VLDB and Partitioning Guide 第5章:管理和维护基于时间的信息 读书笔记

本文为Oracle 19c VLDB and Partitioning Guide第5章Managing and Maintaining Time-Based Information的读书笔记。 Oracle 数据库提供了基于时间管理和维护数据的策略。 本章讨论 Oracle 数据库中的组件&#xff0c;这些组件可以构建基于时间管理和维护数据的策略。 尽管大…

计算机网络复习之网络层

文章目录数据报与虚电路服务的对比IP 协议IP数据报格式IP地址NAT&#xff08;网络地址转换&#xff09;子网划分和子网掩码在支持子网划分的因特网中&#xff0c;路由器如何转发IP数据报无分类编制CIDR构成超网RIP协议OSPF协议ARP协议ICMP协议Ping和Traceroute参考路由选择是网…

Eclipse安装教程

Eclipse安装教程 目录一&#xff0e; 概述二&#xff0e; 下载eclipse三&#xff0e; 安装eclipse四&#xff0e; 使用eclipse。一&#xff0e; 概述 eclipse是针对java编程的集成开发环境&#xff0c;其设计思想是“一切皆插件”。就其本身而言&#xff0c;eclipse只是一个框架…

Hive表的创建,删除,修改

TBLPROPERTIES的主要作用是按键-值对的格式为表增加额外的文档说明。Hive会自动增加两个表属性:一个是last_modified_by&#xff0c;其保存着最后修改这个表的用户的用户名﹔另一个是 last_modified_time&#xff0c;其保存着最后一次修改的新纪元时间秒。用户还可以拷贝一张已…

数据的合并和分组聚合

一&#xff1a;字符串离散化的案列 对于这一组电影数据&#xff0c;如果我们希望统计电影分类(genre)的情况&#xff0c;应该如何处理数据&#xff1f; 思路&#xff1a;重新构造一个全为0的数组&#xff0c;列名为分类&#xff0c;如果某一条数据中分类出现过&#xff0c;就让…

Java之class类

Class类 1.类图 2.Class类对象 系统创建 该class对象是通过类加载器ClassLoader的loadClass()方法生成对应类对应的class对象 通过debug可以追到该方法 3.对于某个类的class类对象 只加载一次 因为类值加载一次 类加载的时机 //1.创建对象实例的时候&#xff08;new&#xf…

7-10 列车调度

火车站的列车调度铁轨的结构如下图所示。 两端分别是一条入口&#xff08;Entrance&#xff09;轨道和一条出口&#xff08;Exit&#xff09;轨道&#xff0c;它们之间有N条平行的轨道。每趟列车从入口可以选择任意一条轨道进入&#xff0c;最后从出口离开。在图中有9趟列车&am…

联合证券|道指狂拉700点!八大科技巨头市值暴涨1.3万亿!

本周最终一个交易日&#xff0c;美股三大指数收盘团体大涨。道指涨2.13%&#xff0c;标普500指数涨2.28%&#xff0c;纳斯达克指数涨2.56%。 大型科技股遍及走强&#xff0c;苹果、亚马逊涨超3%。特斯拉经历惊险一夜&#xff0c;股价盘初跌近8%后反弹&#xff0c;收盘涨幅超2%。…

上半年要写的博客,提前占坑1

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

第二章 创建数据集

2.1 数据集的概念 数据集通常是由数据构成的一个矩形数组&#xff0c;行表示观测&#xff0c;列表示变量。表2-1提供了一个假想的病例数据集。 不同的行业对于数据集的行和列叫法不同。统计学家称它们为观&#xff08;observation&#xff09;和变量&#xff08;variable&…

可变形卷积DCN/DConv

1、定义 可变形卷积是指卷积核在每一个元素上额外增加了一个方向参数&#xff0c;这样卷积核就能在训练过程中扩展到很大的范围&#xff0c;卷积核可以变成任意方向。 图(a)是普通卷积 图(b)、©、(d)是可变形卷积&#xff0c;©(d)是(b)的特例 2、为什么要使用可变…