netty自定义封包实现
文章目录
- 说明
- 分享
- 内置编码器和解码器
- 解码器
- 编码器
- 代码实现
- 创建核心类
- 消息实体类
- 自定义编码类
- 自定义解码类
- 服务端
- ServerHandler
- 入口类
- 客户端
- ClientHandler
- 入口类
- 测试
- 参考
- 总结
说明
netty是java重要的企业级NIO,使用它可以快速实现很多功能通信功能如:http、ftp、socket、websocket、udp等。
本站使用自定义网包实现网络通信。
分享
- 大数据博客列表
- 开发记录汇总
- 个人java工具库 项目https://gitee.com/wangzonghui/object-tool
- 包含json、string、集合、excel、zip压缩、pdf、bytes、http等多种工具,欢迎使用。
内置编码器和解码器
解码器
名称 | 说明 |
---|---|
ByteToMessageDecoder | 如果想实现自己的半包解码器,实现该类 |
MessageToMessageDecoder | 一般作为二次解码器,当我们在 ByteToMessageDecoder 将一个 bytes 数组转换成一个 java 对象的时候,我们可能还需要将这个对象进行二次解码成其他对象,我们就可以继承这个类; |
LineBasedFrameDecoder | 通过在包尾添加回车换行符 \r\n 来区分整包消息; |
StringDecoder | 字符串解码器; |
DelimiterBasedFrameDecoder | 特殊字符作为分隔符来区分整包消息; |
FixedLengthFrameDecoder | 报文大小固定长度,不够空格补全; |
ProtoBufVarint32FrameDecoder | 通过 Protobuf 解码器来区分整包消息; |
ProtobufDecoder | Protobuf 解码器; |
LengthFieldBasedFrameDecoder | 指定长度来标识整包消息,通过在包头指定整包长度来约定包长。 |
编码器
名称 | 说明 |
---|---|
ProtobufEncoder | Protobuf 编码器; |
MessageToByteEncoder | 将 Java 对象编码成 ByteBuf; |
MessageToMessageEncoder | 如果不想将 Java 对象编码成 ByteBuf,而是自定义类就继承这个; |
LengthFieldPrepender | LengthFieldPrepender 是一个非常实用的工具类,如果我们在发送消息的时候采用的是:消息长度字段+原始消息的形式,那么我们就可以使用 LengthFieldPrepender。这是因为 LengthFieldPrepender 可以将待发送消息的长度(二进制字节长度)写到 ByteBuf 的前两个字节。 |
代码实现
创建核心类
消息实体类
public class MyMessage {private int len;//发送内容的长度private byte[] content;//发送的内容public int getLen() {return len;}public void setLen(int len) {this.len = len;}public byte[] getContent() {return content;}public void setContent(byte[] content) {this.content = content;}
}
自定义编码类
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;public class MyMessageEncoder extends MessageToByteEncoder<MyMessage> {@Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, MyMessage myMessage, ByteBuf byteBuf) throws Exception {byteBuf.writeInt(myMessage.getLen());byteBuf.writeBytes(myMessage.getContent());}}
自定义解码类
import java.util.List;import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;public class MyMessageDecoder extends ByteToMessageDecoder {int length=0;@Overrideprotected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {//将二进制字节码转为对象if(byteBuf.readableBytes()>=4){if(length==0){length=byteBuf.readInt();}if(byteBuf.readableBytes()<length){// System.out.println("可读数据不够,继续等待");return;}byte[] content=new byte[length];byteBuf.readBytes(content);MyMessage message=new MyMessage();message.setLen(length);message.setContent(content);list.add(message);//传递给下一个handlerlength=0;}}}
服务端
ServerHandler
import com.netty.cn.rpc.selfmessage.core.MyMessage;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;public class MyServerHandler extends SimpleChannelInboundHandler<MyMessage> {private int count;/*** 读取客户端的数据* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, MyMessage myMessage) throws Exception {System.out.println("服务端收到消息:");System.out.println("长度:"+myMessage.getLen());System.out.println("内容: "+new String(myMessage.getContent(),CharsetUtil.UTF_8));System.out.println("收到消息数量:"+(++count));String msg="服务端收到请求";MyMessage message=new MyMessage();message.setContent(msg.getBytes(CharsetUtil.UTF_8));message.setLen(msg.getBytes(CharsetUtil.UTF_8).length);ctx.writeAndFlush(message);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {super.channelReadComplete(ctx);// 客户端连接进入 FIN_WAIT1 状态// ctx.channel().close();}
}
入口类
import com.netty.cn.rpc.selfmessage.core.MyMessageDecoder;
import com.netty.cn.rpc.selfmessage.core.MyMessageEncoder;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class MyServer {public static void main(String[] args) {int port=8080;EventLoopGroup bossGroup=new NioEventLoopGroup(1);//处理连接请求EventLoopGroup workerGroup=new NioEventLoopGroup();//默认线程数量为cpu核数的两倍,处理业务try {ServerBootstrap bootstrap=new ServerBootstrap();//创建服务器端的启动对象bootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,port).childHandler(new ChannelInitializer<SocketChannel>() {protected void initChannel(SocketChannel socketChannel) {ChannelPipeline channelPipeline=socketChannel.pipeline();channelPipeline.addLast(new MyMessageDecoder());//加解码器channelPipeline.addLast(new MyMessageEncoder());channelPipeline.addLast(new MyServerHandler());}});System.out.println("netty server start");//启动服务器绑定端口,bind是异步操作,sync是等待ChannelFuture cf=bootstrap.bind(port).sync();cf.channel().closeFuture().sync();}catch(Exception e){
// log.error(e.toString(),e);System.out.println(e.toString());}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
客户端
ClientHandler
import com.netty.cn.rpc.selfmessage.core.MyMessage;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;public class MyClientHandler extends SimpleChannelInboundHandler<MyMessage> {/*** 当客户端连接到服务端是触发* @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("连接服务端 "+ctx.channel().remoteAddress()+" 成功");String msg="你好,我是张asdfasdfsadfwerwerwerwerewrewrewrewr三。";for (int i=0;i<20;i++){MyMessage message=new MyMessage();message.setContent(msg.getBytes(CharsetUtil.UTF_8));message.setLen(msg.getBytes(CharsetUtil.UTF_8).length);ctx.writeAndFlush(message);}}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, MyMessage myMessage) throws Exception {System.out.println("client 接收到信息:"+new String(myMessage.getContent()).toString());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
入口类
import com.netty.cn.rpc.selfmessage.core.MyMessageDecoder;
import com.netty.cn.rpc.selfmessage.core.MyMessageEncoder;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class MyClient {public static void main(String[] args) {int port=8080;EventLoopGroup group=new NioEventLoopGroup();try {Bootstrap bootstrap=new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {protected void initChannel(SocketChannel socketChannel) {ChannelPipeline channelPipeline=socketChannel.pipeline();channelPipeline.addLast(new MyMessageDecoder());//加解码器channelPipeline.addLast(new MyMessageEncoder());channelPipeline.addLast(new MyClientHandler());}});//System.out.println("netty client start");//启动客户端连接服务器ChannelFuture cf =bootstrap.connect("127.0.0.1",port).sync();//关闭通道进行监听cf.channel().closeFuture().sync();System.out.println("启动客户端"+port);} catch(Exception e){
// log.error(e.toString(),e);System.out.println(e.toString());}finally {group.shutdownGracefully();}}
}
测试
- 先启动
MyServer
,再启动MyClient
,可以看到控制台打印如下内容: - Server
netty server start
服务端收到消息:
长度:60
内容: 你好,我是张asdfasdfsadfwerwerwerwerewrewrewrewr三。
收到消息数量:1
- Client
连接服务端 /127.0.0.1:8080 成功
client 接收到信息:服务端收到请求
参考
- 博客
总结
- 该方式定义了数据传输结构,传输过程中由编码器
ByteBuf
完成数据处理。 - 由于内容是二进制格式,可以存储数据,如json字符串、protobuf二次处理后数据,提升了数据传输灵活性。
相关文章:

netty自定义封包实现
文章目录说明分享内置编码器和解码器解码器编码器代码实现创建核心类消息实体类自定义编码类自定义解码类服务端ServerHandler入口类客户端ClientHandler入口类测试参考总结说明 netty是java重要的企业级NIO,使用它可以快速实现很多功能通信功能如:http、…...

ORA error集锦
1、oralce 数据客户端需要安装的问题 保存信息为: “无法连接到数据库,因为数据库客户端软件无法加载。确保已正确安装并配置数据库客户端软件” 从百度网盘下载,并安装win32 oracle client 安装包 2、ORA错误 “执行异常,ORA-00911: inval…...

格雷码的实现
格雷码:任意两个相邻的二进制数之间只有一位不同 想必通信专业的学生应该都接触过格雷码,它出现在数电、通信原理等课程里。 如下图所示一个四位格雷码是什么样子的: 格雷码的特点: 其最大的特点是任意上下相邻的两个码值间&am…...

快到金3银4了,准备跳槽的可以看看
前两天跟朋友感慨,今年的铜九铁十、裁员、疫情导致好多人都没拿到offer!现在已经12月了,具体明年的金三银四只剩下两个月。 对于想跳槽的职场人来说,绝对要从现在开始做准备了。这时候,很多高薪技术岗、管理岗的缺口和市场需求也…...

最新BlackArch发布,提供1400款渗透测试工具
近日,BlackArch Linux新版本发布,此版本为白帽子和安全研究人员提供了大约1400款渗透测试工具,如果你是一位白帽子或者安全研究人员,这个消息无疑会让你很感兴趣。BlackArch Linux是一款基于Arch Linux的发行版,主要面…...

重走前端路JS进阶篇:This 指向与箭头函数
JavaScript 高级 This 指向规则 案例 function foo() {console.log(this)}// 1 调用方式1foo();// 2 调用方式2 放入对象中调用var obj {name: "why",foo: foo}obj.foo()// 调用方式三 通过 call/apply 调用foo.call("abc")指向定义 this 是js 给函数的…...

Python基础:函数式编程
一、概述 Python是一门多范式的编程语言,它同时支持过程式、面向对象和函数式的编程范式。因此,在Python中提供了很多符合 函数式编程 风格的特性和工具。 二、lambda表达式(匿名函数) 除了 函数 中介绍的 def语句,P…...

【YBT2023寒假Day14 C】字符串题(SAM)(树链剖分)(线段树)
字符串题 题目链接:YBT2023寒假Day14 C 题目大意 对于一个字符串 S 定义 F(S) 是 fail 树上除了 0 点其它点的深度和。 G(S) 是 S 每个子串 S’ 的 F(S’) 之和。 然后一个空串,每次在后面加一个字符,要你维护这个串的 G 值。 思路 考虑…...

Tailwind CSS 在Vue中的使用
什么是Tailwind CSS? Tailwind CSS 是一个功能类优先的 CSS 框架,它集成了诸如 flex, pt-4, text-center 和 rotate-90 这样的的类,支持 hover 和 focus 样式,它们能直接在脚本标记语言中组合起来,构建出任何设计。 …...

三层楼100人办公网络如何规划设计实施(实战案例)
如何设计组网 1.采用防火墙+三层交换机+二层POE交换机+AP的方案 2.三层交换机作为网络的核心,提供网络的配置、划分和各个VLAN间的数据交换,而每个VLAN由二层交换机组建 3.网络主干设备的选型,建议网络主干设备或核心层设备选择具备第3层交换功能的高性能主干交换机。 4…...

Redis:实现全局唯一ID
Redis:实现全局唯一ID一. 概述二. 实现(1)获取初始时间戳(2)生成全局ID三. 测试为什么可以实现全局唯一?其他唯一ID策略补充:countDownLatch一. 概述 全局ID生成器:是一种在【分布式…...

webpack打包基本原理——实现webpack打包核心功能
webpack打包的基本原理 核心功能就是把我们写的模块化代码转换成浏览器能够识别运行的代码,话不多说我们一起来了解它 首先我们建一个空项目用 npm init -y 创建一个初始化的,在跟目录下创建src文件夹,src下创建index.js,add.js…...

git的使用(终端输入指令) 上
git目录前言1.创建仓库2.创建文件和修改数据状态分区3 .删除、撤销重置 、和比较前言 今天带大家手把手敲一遍 git 流程: 安装一下git(详细观看我之前发的git文档࿰…...

react定义css样式,使用less,css模块化
引入外部 css文件 import ./index.css此时引入的样式是全局样式 使用less 安装 npm i style-loader css-loader sass-loader node-sass -D生成config文件夹 npm run eject配置 以上代码运行完,会在根目录生成config文件夹 进入 config > webpack.config.js 查找…...

基于JavaWeb的学生管理系统
文章目录 项目介绍主要功能截图:登录用户信息管理院系信息管理班级信息管理新增学生课程管理成绩管理部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系�…...

win11右键新建菜单添加选项
需要操作 2 处注册表, 以下以在右键新建菜单中添加 .html 为例 在主键 HKEY_CLASSES_ROOT 中,搜索 .html 找到后 ,右键点击它,选 新建 ->项, 在这里插入图片描述 项目名字是:ShellNew 新建后&#x…...

leetcode Day5(卡线复试,放弃版)
Day5 最后一个单词长度(要求最后一个,可以反向计数) int lens.length()-1; while(s.charAt(len)){len--;//最后是一个空格,就是无字符时 } int wordlen0;//记录字符长度 /*charAt() 方法用于返回指定索引处的字符。索引范围为从 0…...

cmake 入门二 库的编译,安装与使用
工程描述 1,建立一个静态库和动态库,提供HelloFunc 函数供其他程序编程使用,HelloFunc 向终端输出Hello World字符串。 2,安装头文件与共享库。 1 库的工程结构 1.1 工程目录下的CMakeLists.txt PROJECT…...

Python中实现将内容进行base64编码与解码
一、需求说明需要使用Python实现将内容转为base64编码,解码,方便后续的数据操作。二、base64简介Base64是一种二进制到文本的编码方式【是一种基于 64 个可打印字符来表示二进制数据的表示方法(由于 2^664,所以每 6 个比特为一个单…...

集合TreeSet的使用-java
TreeSet的特点:可排序、不重复、无索引。可排序:按照元素的大小默认升序排序;底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。对于数值、字符串类型的(Integer 、Double、String)TreeSet可以排…...

Mybatis-plus 分页集成以及基本使用总结 入门和案例 注解连表查询分页案例等
简介 Mybaits-plus 是mybits 的升级版,从mybaits 升级到mybaits-plus 可以实现平滑升级 Mybaits-plus 本身提供了大量的基本查询方法以及强大的 Wrapper(包装) 类 用于查询的 QueryWrapper 以及 更新的 UpdateWrapper ,使用Wrapper 基本已经可以构建大…...

5个设计师常用素材库
推荐5个设计素材网站,免费下载! 1、菜鸟图库 菜鸟图库-免费设计素材下载 菜鸟图库是一个素材量非常丰富的网站,该网站聚合了平面、UI、淘宝电商、高清背景图、图片、插画等高质量素材。平面设计模板非常多,很多都能免费下载&…...

PHP/7.2.11 缺少 apache2/logs/httpd.pid 文件
启动服务时:systemctl restart httpd.service,报错:● httpd.service - httpd serviceLoaded: loaded (/etc/systemd/system/httpd.service; enabled; vendor preset: disabled)Active: failed (Result: exit-code) since 五 2023-02-24 16:1…...

【centos7下部署mongodb】
一.安装环境 CentOS7MongoDB4.0.13正式版。 二.下载MongoDB 1.1 官网下载地址:https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.13.tgz 1.2 将压缩包通过xftp上传到服务器/opt目录,然后解压、改名 三. 配置环境变量及配置文件 3.1配置系…...

pycharm首次使用爬虫框架scrapy遇到的问题和解决方法(二)
在首次使用scrapy框架的过程中,一直是对着别人的框架步骤撸代码的。然而,撸着撸着发现好像别人的也用不了。后面就只能自己找踏坑了。 问题报错: 1,IndexError: list index out of range 2,pymysql.err.ProgrammingError: (1064, "You have an error in your SQL s…...

pyflink学习笔记(二):table_apisql
Joins Inner Join 官网说明:和 SQL 的 JOIN 子句类似。关联两张表。两张表必须有不同的字段名,并且必须通过 join 算子或者使用 where 或 filter 算子定义至少一个 join 等式连接谓词。先创建2个表,两个表的字段是相同的,我想验证…...

嵌入式 STM32 实现STemwin移植+修改其配置文件,驱动LCD显示文本 (含源码,建议收藏)
目录 一、STemwin 简介 二、源码下载 1、在移植STemwin源码之前,需要一个已经具备LCD读写,填充指定颜色等函数功能的一个工程; 2、STemwin 3、源码下载 三、STemwin移植 1、解压源码路径 2、STemwin文件介绍 四、修改配置文件&…...

[计算机网络(第八版)]第一章 概述(学习笔记)
1.1 计算机网络在信息时代中的作用 21世纪是以网络为核心的信息时代,21世纪的重要重要特征:数字化、网络化与信息化。 三大类网络 电信网络:向用户提供电话、电报、传真等服务;有线电视网络:向用户传送各种电视节目&am…...

AI绘图:常用镜头和视角
镜头 Establishing Shot 通过宽广或超宽广的视角交待故事发生的大地理环境 Master Shot 众多人物都能完整地出现在镜头里。 Wide Shot 广角镜头。又称“长镜头” Long Shot。人物全身展现,但在画面中所占比例相对较少 Ultrawide Shot 超广角镜头 Low Angle Sho…...

TCP四次挥手
TCP 四次挥手过程是怎样的? TCP 断开连接是通过四次挥手方式。 双方都可以主动断开连接,断开连接后主机中的「资源」将被释放,四次挥手的过程如下图: 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1…...