Netty各组件基本用法、入站和出站详情、群聊系统的实现、粘包和拆包

Netty

  • Bootstrap和ServerBootstrap
  • Future和ChannelFuture
  • Channel
  • Selector
  • NioEventLoop和NioEventLoopGroup
  • ByteBuf
    • 示例代码
  • Channel相关组件
  • 入站详情
  • 出站详情
  • 对象编解码
  • ProtoBuf和ProtoStuff
  • netty实现群聊系统
  • 粘包和拆包
    • TCP协议特点
    • 举个例子


Bootstrap和ServerBootstrap

Bootstrap是Netty的启动程序,一个Netty应用通常由一个Bootstrap开始。Bootstrap的主要作用是配置Netty程序,串联Netty的各个组件。
在这里插入图片描述


Future和ChannelFuture

在这里插入图片描述这个方法是异步的(交给别的线程去执行该任务),当执行到这之后,netty不一定启动了

ChannelFuture channelFuture = bootstrap.bind(9090);
channelFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture funture) throws Exception {if(funture.isSuccess()){System.out.println("监听9090成功");}else{System.out.println("监听9090失败");}}
});

该方法可以知晓有没有启动成功,或者改为同步的方式
ChannelFuture channelFuture = bootstrap.bind(9090).sync();

在这里插入图片描述
ChannelFuture和Future的子类,提供了针对于Channel的异步监听操作


Channel

NioSocketChannel:异步的客户端才CP Socket连接通道
NioServerSocketChannel:异步的服务端TCP Socket连接通道
NioDatagramChannel:异步的UDP连接通道
NioSctpChannel:异步的客户端Sctp连接通道
NioSctpServerChannel:异步的服务端Sctp连接通道


Selector

通过Selector多路复用器实现IO的多路复用。Selector可以监听多个连接的Channel事件,同时可以不断的查询已注册Channel是否处于就绪状态,实现一个线程可以高效的管理多个Channel。


NioEventLoop和NioEventLoopGroup

NioEventLoop本质就是一个线程,一个NioEventLoop就对应一个线程,但可以达到异步的处理事务,因为NioEventLoop内部维护了一个异步任务队列,用于存储需要在事件循环中执行的任务。通过将任务添加到队列中,NioEventLoop可以在空闲时间执行这些任务,从而实现了异步提交事务的能力。
NioEventLoopGroup管理NioEventLoop的生命周期,可以理解为是线程池,内部维护了一组线程。每个线程(即NioEventLoop)负责处理多个Channel上的事件。注意,一个Channel只对应一个线程,NioEventLoop和Channel它们是一对多的关系。
一个线程可以管理多个channel,但一个channel只能被一个线程执行


ByteBuf

初始情况
在这里插入图片描述

写入数据
在这里插入图片描述

读取数据
在这里插入图片描述
已读的区域:[0,readerIndex]
可读的区域:[readIndex,writeIndex)
可写的区域:[writeIndex,capacity)

示例代码

public class ByteBufDemo {public static void main(String[] args) {//创建一个有10个容量数据的ByteBuf对象ByteBuf buf = Unpooled.buffer(10);System.out.println("init buf:"+buf);//添加数据for(int i = 0;i<5;i++){buf.writeByte(i);}System.out.println("after write:"+buf);//按照索引读取数据for(int i = 0;i<3;i++){System.out.println(buf.getByte(i));}System.out.println("after get:"+buf);//读取数据for(int i = 0;i<3;i++){System.out.println(buf.readByte());}System.out.println("after read:"+buf);}
}

Channel相关组件

在这里插入图片描述

ChannelHandler
ChannelHandler用于处理拦截IO事件,往往在ChannelHandler中可以加入业务处理逻辑。ChannelHandler执行完后会将io事件转发到ChannelPipeline中的下一个处理程序。
ChannelHandlerContext
保存Channel相关的上下文信息,并关联一个ChannelHandler对象。
ChannelPipeline
ChannelPipeline是一个双向链表,其中保存着多个ChannelHandler。ChannelPipeline实现了一种高级形式的过滤器模式,在IO操作时发生的入站和出站事件,会导致ChannelPipeline中的多个ChannelHandler被依次调用。
在这里插入图片描述


入站详情

在这里插入图片描述
在这里插入图片描述
现在我们的客户端和服务端之间就有三个拦截器
我们在NettyServerHandler里面收到信息就不用解码了,为什么,因为解码器的拦截器已经帮我们做好了

在这里插入图片描述
当我们服务端读数据的时候,会从客户端读数据==入站,因为解码的handler和业务处理的handler是入站拦截器,所以会对数据产生作用,但编码的handler不会,因为它是一个出站handler


出站详情

站在服务端的立场
在这里插入图片描述
在Netty中,客户端和服务端的addLast方法有一些不同之处。具体来说,它们的区别如下:
1. 顺序:当调用addLast方法添加处理器时,它们的顺序略有不同。对于客户端来说,添加的处理器是按照添加的顺序进行顺序执行的,即先添加的处理器先执行。而对于服务端来说,添加的处理器是按照逆序执行的,即先添加的处理器后执行。
2. 作用对象:客户端的addLast方法主要作用于Outbound事件,用于处理从客户端发送到服务端的请求。而服务端的addLast方法主要作用于Inbound事件,用于处理从服务端接收到的请求。
3. 处理逻辑:客户端和服务端的addLast方法所添加的处理器,通常具有不同的处理逻辑。客户端的处理器通常用于编码请求、发送请求等操作。服务端的处理器通常用于解码请求、处理请求、编码响应等操作。


对象编解码

对象编码器
在这里插入图片描述
对象解码器
在这里插入图片描述


ProtoBuf和ProtoStuff

为了编解码提升性能,可以使用Protobuf域者Protpstuff对数据进行序列话和反序列化,效率更高。

第一步:导入依赖

<dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-api</artifactId><version>1.0.10</version>
</dependency><dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.0.10</version>
</dependency><dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-runtime</artifactId><version>1.0.11</version>
</dependency>

第二步:引入工具类

public class ProtostuffUtils {/*** 避免每次序列化都重新申请Buffer空间*/private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);/*** 缓存Schema*/private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>();/*** 序列化方法,把指定对象序列化成字节数组** @param obj* @param <T>* @return*/@SuppressWarnings("unchecked")public static <T> byte[] serialize(T obj) {Class<T> clazz = (Class<T>) obj.getClass();Schema<T> schema = getSchema(clazz);byte[] data;try {data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);} finally {buffer.clear();}return data;}/*** 反序列化方法,将字节数组反序列化成指定Class类型** @param data* @param clazz* @param <T>* @return*/public static <T> T deserialize(byte[] data, Class<T> clazz) {Schema<T> schema = getSchema(clazz);T obj = schema.newMessage();ProtostuffIOUtil.mergeFrom(data, obj, schema);return obj;}@SuppressWarnings("unchecked")private static <T> Schema<T> getSchema(Class<T> clazz) {Schema<T> schema = (Schema<T>) schemaCache.get(clazz);if (Objects.isNull(schema)) {//这个schema通过RuntimeSchema进行懒创建并缓存//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的schema = RuntimeSchema.getSchema(clazz);if (Objects.nonNull(schema)) {schemaCache.put(clazz, schema);}}return schema;}
}

netty实现群聊系统

服务端基本代码

//群聊系统的服务器
public class ChatServer {public static void main(String[] args) throws Exception {EventLoopGroup boosGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();ServerBootstrap serverBootstrap = new ServerBootstrap();//配置参数serverBootstrap.group(boosGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//获得piplelineChannelPipeline pipeline = ch.pipeline();//添加handlerpipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());//添加业务处理handlerpipeline.addLast(new ChatServerHandler());}});System.out.println("聊天室启动了...");ChannelFuture channelFuture = serverBootstrap.bind(9090).sync();channelFuture.channel().closeFuture().sync();boosGroup.shutdownGracefully();workerGroup.shutdownGracefully();}
}

服务端业务代码

public class ChatServerHandler extends SimpleChannelInboundHandler<String>{private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//存放Channel的容器,而且还可以执行对每个channel执行的任务private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//有客户端上线了//有新的客户端连接了,将该客户端的上线信息广播给其它所有客户端@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//得到客户端的channelChannel channel = ctx.channel();String message = "客户端-"+channel.remoteAddress()+"于"+sdf.format(new Date())+"上线了\n";//得到其它客户端的channel向其它客户端发送该客户端的channelchannelGroup.writeAndFlush(message);//加入到channelGroup中channelGroup.add(channel);}/** 客户端下线则广播给其它客户端*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();//生成一个下线的信息String message = "客户端-"+channel.remoteAddress()+"于"+sdf.format(new Date())+"下线了\n";//广播给其它客户端channelGroup.writeAndFlush(message);}/**具体读数据的业务 */@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {//获得当前发消息的客户端channelChannel channel =   ctx.channel();//遍历所有的channelchannelGroup.forEach(ch->{if(channel!=ch){ch.writeAndFlush("客户端-"+channel.remoteAddress()+"于"+sdf.format(new Date())+"说:"+msg+"\n");}else{ch.writeAndFlush("我于"+sdf.format(new Date())+"说:"+msg+"\n");}});}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}
}

客户端基本代码

public class ChatClient {public static void main(String[] args) throws Exception {EventLoopGroup eventLoopGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());pipeline.addLast(new ChatClientHandler());}});//发送消息ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9090).sync();Channel channel = channelFuture.channel();System.out.println("欢迎进入Yc聊天室");Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String message = scanner.nextLine();channel.writeAndFlush(message);}eventLoopGroup.shutdownGracefully();}
}

客户端业务代码

public class ChatClientHandler extends SimpleChannelInboundHandler<String> {//打印在控制台@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {System.out.println(s);}
}

粘包和拆包

TCP协议特点

作为一个流式传输协议,数据在TCP中传输是没有边界的。也就是说,客户端发送的多条数据,有可能会被认为是一条数据。或者,客户端发送的一条数据,有可能会被分成多条数据。这是由于TCP协议并不了解上层业务数据的具体含义,在使用TCP协议传输数据时,是根据TCP缓冲区的实际情况进行数据包的划分。

举个例子

我们要发两句话
我是杨 他是李
可能别人收到的信息就是我是杨他是李一条数据,也可能收到我是 杨他是李这两句话
假设我们这有个客户端

在这里插入图片描述
发送两百次消息
在这里插入图片描述
就可能得到这样的结果
粘包:缓冲区还可以放的下 拆包:缓冲区不可以放的下(乱码发生的原因是因为一个字的字节放在不同缓冲区内发送)

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

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

相关文章

css定义超级链接a标签里面的title的样式

效果: 代码: 总结:此css 使用于任何元素,不仅仅是a标签!

Redis如何实现排行榜?

今天给大家简单聊聊 Redis Sorted Set 数据类型底层的实现原理和游戏排行榜实战。特别简单&#xff0c;一点也不深入&#xff0c;也就 7 张图&#xff0c;粉丝可放心食用&#xff0c;哈哈哈哈哈~~~~。 1. 是什么 Sorted Sets 与 Sets 类似&#xff0c;是一种集合类型&#xff…

F5 LTM 知识点和实验 7-使用SNATs处理流量

第七章:使用SNATs处理流量 SNATs: 传统的vs都是对目的地址和端口进行改变,而源地址没有改变,如果你需要对源地址和源端口进行更改,则需要使用SNAT能力,好处在于: 1、允许不可路由地址(网络内部)的设备获得可路由地址以进入网络外部。2、确保目标服务器通过BIG-IP系统返…

Qt应用开发(基础篇)——QComboBox

目录 一、前言 二、属性和方法 三、信号 四&#xff1a;QFontComboBox 一、前言 QComboBox继承于QWidget&#xff0c;作为Qt Wdiget常用的控件&#xff0c;在实际开发中&#xff0c;经常用来作为某些特定参数属性的选择&#xff0c;比如语言、国家、字体、主题…

【Rust笔记】意译解构 Object Safety for trait

意译解构Object Safety for trait 借助【虚表vtable】对被调用成员函数【运行时内存寻址】的作法允许系统编程语言Rust模仿出OOP高级计算机语言才具备的【专用多态Ad-hoc Polymorphism】特性。 计算机高级语言中的“多态”术语是一个泛指。它通常可被细化为 基于继承关系的“子…

配置IPv6 over IPv4 GRE隧道示例

组网需求 如图1&#xff0c;两个IPv6网络分别通过SwitchA和SwitchC与IPv4公网中的SwitchB连接&#xff0c;客户希望两个IPv6网络中的PC1和PC2实现互通。 其中PC1和PC2上分别指定SwitchA和SwitchC为自己的缺省网关。 图1 配置IPv6 over IPv4 GRE隧道组网图 配置思路 要实现I…

Flyway——修改表名称与序列名称

文章目录 前言脚本修改表名称修改序列 前言 开发中一次偶然的机会&#xff0c;Oracle 12c 更换为 11g &#xff0c;需要对表名称的长度和序列长度做限制要求。 11g相对12c而言&#xff0c;表名称与序列名称的长度&#xff0c;不能超过30个字符。 在开发中做了更改&#xff0c;…

Tinkercad 建模21个小技巧

21个Tinkercad 建模小技巧 原文 参考文章&#xff1a;在 Tinkercad 中加快设计的 22 个技巧 一起来了解一下21个Tinkercad 3D建模小技巧&#xff0c;让你快人一步。 技巧1 Copy & Paste 文件&#xff0c;整合设计 想把文件A里面的模型拷贝到文件B里面&#xff1f; 很容…

找不到Windows SDK版本 10.0.18362.0.请安装所需版本的 Windows SDK,或者在项目属性页中或通过右键单击解决

找不到Windows SDK版本 10.0.18362.0.请安装所需版本的 Windows SDK,或者在项目属性页中或通过右键单击解决 安装相应的组件 项目——重定目标解决方案——然后选择版本

商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

&#xfeff; 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Re…

【软件安装】MATLAB_R2021b for mac 安装

Mac matlab_r2021b 安装 下载链接&#xff1a;百度网盘 下载链接中所有文件备用。 我所使用的电脑配置&#xff1a; Macbook Pro M1 Pro 16512 系统 macOS 13.5 安装步骤 前置准备 无此选项者&#xff0c;自行百度 “mac 任何来源”。 1 下载好「MATLAB R2021b」安装文…

Jetson Nano之ROS入门 -- YOLO目标检测与定位

文章目录 前言一、yolo板端部署推理二、目标深度测距三、目标方位解算与导航点设定1、相机成像原理2、Python实现目标定位 总结 前言 Darknet_ros是一个基于ROS&#xff08;机器人操作系统&#xff09;的开源深度学习框架&#xff0c;它使用YOLO算法进行目标检测和识别。YOLO算…

代理模式:控制访问的设计模式

代理模式&#xff1a;控制访问的设计模式 什么是代理模式&#xff1f; 代理模式是一种常见的设计模式&#xff0c;它允许通过代理对象来控制对真实对象的访问。代理模式的主要目的是在不改变原始对象的情况下&#xff0c;提供额外的功能或控制访问。 为什么要使用代理模式&a…

2023年Q2京东环境电器市场数据分析(京东数据产品)

今年Q2&#xff0c;环境电器市场中不少类目表现亮眼&#xff0c;尤其是以净水器、空气净化器、除湿机等为代表的环境健康电器。此外&#xff0c;像冷风扇这类具有强季节性特征的电器也呈现出比较好的增长态势。 接下来&#xff0c;结合具体数据我们一起来分析Q2环境电器市场中…

所有流的知识都有,IO流原理及流的分类

1、Java IO流原理 I/O是Input/Output的缩写&#xff0c; I/O技术是非常实用的技术&#xff0c;用于处理设备之间的数据传输。如读/写文件&#xff0c;网络通讯等。 Java程序中&#xff0c;对于数据的输入/输出操作以”流(stream)” 的方式进行。java.io包下提供了各种“流”类…

微信小程序监测版本更新

在index.js里面 不放到app.js里面是因为有登录页面&#xff0c;在登录页面显示更新不太友好 onShow() {const updateManager wx.getUpdateManager()// 请求完新版本信息的回调updateManager.onCheckForUpdate(res > {if (res.hasUpdate) {// 新版本下载成功updateManage…

【JavaEE初阶】Servlet (二) Servlet中常用的API

文章目录 HttpServlet核心方法 HttpServletRequest核心方法 HttpServletResponse核心方法 Servlet中常用的API有以下三个: HttpServletHttpServletRequestHttpServletResponse HttpServlet 我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其…

以科技创新引领短交通行业发展,九号公司重磅新品亮相巴塞罗那MWC

2月27日&#xff0c;以“时不我待(VELOCITY) - 明日科技&#xff0c;将至已至”为主题的2023世界移动通信大会&#xff08;Mobile World Congress&#xff0c;以下简称MWC&#xff09;在西班牙巴塞罗那举办&#xff0c;全球创新短交通领军企业九号公司参加了大会。现场&#xf…

2023年FPGA好就业吗?

FPGA岗位有哪些&#xff1f; 从芯片设计流程来看&#xff0c;FPGA岗位可以分四类 产品开发期&#xff1a;FPGA系统架构师 芯片设计期&#xff1a;数字IC设计工程师、FPGA开发工程师 芯片流片期&#xff1a;FPGA验证工程师 产品维护期&#xff1a;FAE工程师 从行业上来说&#x…

6、Kubernetes核心技术 - Pod

目录 一、概述 二、Pod机制 2.1、共享网络 2.2、共享存储 三、Pod资源清单 四、 Pod 的分类 五、Pod阶段 六、Pod 镜像拉取策略 ImagePullBackOff 七、Pod 资源限制 八、容器重启策略 一、概述 Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。P…