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可以排…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...
