netty自定义封包实现

news/2024/3/29 1:31:45/文章来源:https://blog.csdn.net/qq_22973811/article/details/129204762

文章目录

  • 说明
  • 分享
  • 内置编码器和解码器
    • 解码器
    • 编码器
  • 代码实现
    • 创建核心类
      • 消息实体类
      • 自定义编码类
      • 自定义解码类
    • 服务端
      • 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 解码器来区分整包消息;
ProtobufDecoderProtobuf 解码器;
LengthFieldBasedFrameDecoder指定长度来标识整包消息,通过在包头指定整包长度来约定包长。

编码器

名称说明
ProtobufEncoderProtobuf 编码器;
MessageToByteEncoder将 Java 对象编码成 ByteBuf;
MessageToMessageEncoder如果不想将 Java 对象编码成 ByteBuf,而是自定义类就继承这个;
LengthFieldPrependerLengthFieldPrepender 是一个非常实用的工具类,如果我们在发送消息的时候采用的是:消息长度字段+原始消息的形式,那么我们就可以使用 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二次处理后数据,提升了数据传输灵活性。

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGIS Pro二次开发】(7):地图(Map)的基本操作

地图是ArcGIS Pro中的基础起点&#xff0c;也是大多数工程的基础。主要用于显示表示空间数据的图层。 一、地图(Map)的基本操作示例 1、获取当前地图 var map MapView.Active.Map; 2、获取一级图层 var lys map.Layers; 用于获取地图中的单一图层&#xff0c;以及图层组…

深入了解Java线程锁(一)

在上一篇《如何保证线程的原子性》中&#xff0c;我们谈到了锁&#xff08;Synchronized&#xff09;&#xff0c; 这次我们就来深入探讨一下Java多线程中的锁。 互斥锁的本质是共享资源。 如上图所示&#xff0c; Thread1访问受保护资源&#xff0c;对其加锁&#xff0c;将…

【GO】k8s 管理系统项目16[前端部分–前端布局]

【GO】k8s 管理系统项目[前端部分–前端布局] 1. 前端布局 2. Layout 2.1 layout src/layout/Layout.vue <template><div class"common-layout"><el-container><el-side width"200">Aside</el-side><el-container>…

CAN总线开发一本全(3) - 微控制器集成的FlexCAN外设

CAN总线开发一本全&#xff08;3&#xff09; - 微控制器集成的FlexCAN外设 苏勇&#xff0c;2023年2月 文章目录CAN总线开发一本全&#xff08;3&#xff09; - 微控制器集成的FlexCAN外设引言硬件外设模块系统概要总线接口单元 - 寄存器清单数据结构 - 消息缓冲区MB初始化过…

React(一):初识React、类组件、jsx的基础语法

React&#xff08;一&#xff09;一、初识React1.简单介绍2.React的三个依赖3.Hello React案例二、类组件1.定义类组件并渲染2.绑定事件函数&#xff08;奇怪的this问题&#xff09;3.数组形式数据的展示&#xff08;电影案例&#xff09;4.计数器案例三、jsx语法详解1.jsx的书…

利用InceptionV3实现图像分类

最近在做一个机审的项目&#xff0c;初步希望实现图像的四分类&#xff0c;即&#xff1a;正常&#xff08;neutral&#xff09;、涉政&#xff08;political&#xff09;、涉黄&#xff08;porn&#xff09;、涉恐&#xff08;terrorism&#xff09;。有朋友给推荐了个github上…

机器学习笔记之近似推断(一)从深度学习角度认识推断

机器学习笔记之近似推断——从深度学习角度认识推断引言推断——基本介绍精确推断难的原因虽然能够表示&#xff0c;但计算代价太大无法直接表示引言 本节是一篇关于推断总结的博客&#xff0c;侧重点在于深度学习模型中的推断任务。 推断——基本介绍 推断(Inference\text{…

Python中实现将内容进行base64编码与解码

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

国产音质好的蓝牙耳机有哪些?国产音质最好的耳机排行

随着时间的推移&#xff0c;真无线蓝牙耳机逐渐占据耳机市场的份额&#xff0c;成为人们日常生活中必备的数码产品之一。蓝牙耳机品牌也多得数不胜数&#xff0c;哪些国产蓝牙耳机音质好&#xff1f;下面&#xff0c;我们从音质出来&#xff0c;来给大家介绍几款国产蓝牙耳机&a…

硬件系统工程师宝典(11)-----去耦电容布局“有讲究”

各位同学大家好&#xff0c;欢迎继续做客电子工程学习圈&#xff0c;今天我们继续来讲这本书&#xff0c;硬件系统工程师宝典。 上篇我们说到在电源完整性分析的目标就是要做到电源的干净、稳定和快速响应&#xff0c;以及针对不同噪声处理的实现方法。今天我们来看看去耦电容…

父传子与子传父步骤

父传子&#xff1a; 问题&#xff1a;父页面中引入子组件 把想要传给子页面的值用在子组件中用 &#xff1a;值“值” (用同一个值好区分)来绑定。 在子页面中用props接收 子组件不能改变父组件传过来的值。&#xff08;传多个页面的时候是&#xff0c;比如父传孙的时候我会…

【2023】华为OD机试真题Java-题目0221-AI处理器组合

AI处理器组合 题目描述 某公司研发了一款高性能AI处理器。每台物理设备具备8颗AI处理器,编号分别为0、1、2、3、4、5、6、7。编号0-3的处理器处于同一个链路中,编号4-7的处理器处于另外一个链路中,不通链路中的处理器不能通信,如下图所示。现给定服务器可用的处理器编号数…

STM32---备份寄存器BKP和 FLASH学习使用

BKP库函数 学习BKP&#xff0c;首先就是知道BKP每一个函数的作用然后如何使用即可 使用备份域的作用只需要操作上面的两个函数即可&#xff0c;其余的都是它的其他功能 BKP简介 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份…

如何高效管理自己的时间,可以从这几个方向着手

如果你是上班族&#xff0c;天选打工人&#xff0c;你的绝大多数时间都属于老板&#xff0c;能够自己支配的时间其实并不多&#xff0c;所以你可能察觉不到时间管理的重要性。但如果你是自由职业者或者创业者&#xff0c;想要做出点成绩&#xff0c;那你就需要做好时间管理&…

2. Dart 开发工具环境配置

很多编辑器都可以用来开发dart&#xff0c;所以大家可以选择自己喜欢的编辑器去进行开发。我还是比较喜欢vs code如果你不用vs code来开发dart的话&#xff0c;这篇文章可以直接跳过。如果想要在vs code里有dart的语法提示&#xff0c;我们需要安装相关的插件如图点开插件输入d…

预编译(回顾)

浏览器运行机制变量提升机制私有变量提升步骤全局对象 GO 和全局变量对象 VO的区别带var和不带var的区别系统输出顺序变量提升在条件判断下的处理防止函数的变量提升 浏览器运行机制 为了能够让代码执行&#xff0c;浏览器首先会形成一个执行环境栈 ECStack(Execution Contex…

TCP协议和TCP连接

TCP协议和TCP连接一、TCP协议的简介二、TCP连接的简介1、TCP连接的建立&#xff08;TCP三次握手&#xff09;2、TCP连接的断开&#xff08;TCP四次挥手&#xff09;一、TCP协议的简介 TCP&#xff08;Transmission Control Protocol&#xff09;传输控制协议是一种面向连接的、…

java 接口 详解

目录 一、概述 1.介绍 : 2.定义 : 二、特点 1.接口成员变量的特点 : 2.接口成员方法的特点 : 3.接口构造方法的特点 : 4.接口创建对象的特点 : 5.接口继承关系的特点 : 三、应用 : 1.情景 : 2.多态 : ①多态的传递性 : ②关于接口的多态参数和多态…

股票量化交易SQL特征工程入门

虽然现在各种量化教程和自助平台铺天盖地&#xff0c;但是对于新人来说入门最重要的事情就是挖掘特征。 对于传统的学习路径第一步是学习Python或者某一门编程语言&#xff0c;虽说Python入门容易上手快&#xff0c;但是要在实际应用中对股票数据进行分析&#xff0c;并挖掘有…

2023/2/24 图数据库Neo4j的理解与应用

1 什么是图数据库&#xff08;graph database&#xff09; 十大应用案例&#xff1a;https://go.neo4j.com/rs/710-RRC-335/images/Neo4j-Top-Use-Cases-ZH.pdf “大数据”每年都在增长&#xff0c;但如今的企业领导者不仅需要管理更大规模的数据&#xff0c;还迫切需要从现有…