Netty(2)-编写简单的Netty应用程序

本篇将讲述如何书写简单的Netty服务端和客户端。

1. 编写服务端

所有的Netty服务器都需要以下两部分:

  • 至少一个ChannelHandler,该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑。
  • 引导,配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的端口上。

1.1 ChannelHandler和业务逻辑

ChannelHandler是一个接口族的父接口,它负责接收并响应事件通知。在netty应用程序中,所有的数据处理逻辑都包含在这些核心抽象的实现中。

服务器要响应传入的消息,需要实现ChannelInboundHandler接口,用来定义响应入站事件的方法。一般继承ChannelInboundHandlerAdapter类就足够了,它提供了ChannelInboundHandler的默认实现。

接口中比较重要的方法有:

  • ChannelRead(),对于每个传入的消息都要调用。
  • ChannelReadComplete(),通知 ChannelInboundHandler最后一次对ChannelRead()的调用是当前批量读取中的最后一条消息。
  • exceptionCaught(),在读取操作期间,有异常抛出时会调用。

对应的服务端Handler代码如下:


// 标识一个ChannelHandler可以被多个Channel安全地共享
@ChannelHandler.Sharable
public class EasyNettyServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf input=(ByteBuf) msg;// 打印来自客户端的信息System.out.println("服务端接受请求:" +input.toString(StandardCharsets.UTF_8));// 将接收到的消息写给发送者,而不冲刷出站消息ctx.write(input);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// 将未决消息冲刷到远程节点,并关闭该Channelctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 打印异常堆栈,正式项目建议用logger组件打印cause.printStackTrace();// 关闭该channel,关闭连接ctx.close();}
}

如果不捕获异常,会发生什么?

每个channel都拥有一个与之相邻的ChannelPipeline,其持有一个ChannelHandler的实例链。在默认情况下,ChannelHandler会把对它的方法的调用转发给链中的下一个ChannelHandler。如果exceptionCaught()方法没有被链中的某处实现,那么所接收的异常将被传递到ChannelPipeline的尾端并被记录。因此,我们的应用程序中应该提供至少一个实现了exceptionCaught()方法的ChannelHandler.

除了ChannelInboundHandlerAdapter外,还有很多需要学习的ChannelHandler的子类型和实现。当前我们需要记住以下关键点:

  • 针对不同类型的事件来调用ChannelHandler;
  • 应用程序通过实现或者扩展ChannelHandler来挂钩到事件的生命周期,并提供自定义的应用程序逻辑;
  • 在架构上,ChannelHandler有助于保持业务逻辑与网络处理代码的分离,简化了开发过程。 

1.2 引导服务器

EasyNettyServerHandler 实现核心业务逻辑后,就可以初始化服务器了,具体步骤如下:

  • 绑定到服务器将在其监听并接受传入连接请求的端口;
  • 配置Channel,以将有关的入站消息通知给EasyNettyServerHandler 实例。

传输

在网络协议中的标准多层视图中,传输层提供了端到端的或者主机到主机的通信服务。

因特网通信建立在TCP传输之上。除了一些由Java NIO实现提供的服务器端性能增强外,NIO传输大多数时候指的就是TCP传输。

 服务端实例代码如下:


@Slf4j
public class EasyNettyServer {public static final int PORT = 4700;public static void main(String[] args) throws Exception {new EasyNettyServer().start();}public void start() throws Exception {final EasyNettyServerHandler serverHandler = new EasyNettyServerHandler();// 创建EventLoopEventLoopGroup group = new NioEventLoopGroup();try {// 创建ServerBootstarpServerBootstrap bootstrap = new ServerBootstrap();// 指定所使用的NIO传输Channel;使用指定的端口设置套接字地址;添加到一个Handler到子Channel的ChannelPipelinebootstrap.group(group).channel(NioSctpServerChannel.class).localAddress(new InetSocketAddress(PORT)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// EasyNettyServerHandler被标注为@Sharable,所以总能使用同样的实例socketChannel.pipeline().addLast(serverHandler);}});// 异步地绑定服务器;调用sync()方法阻塞等到直到绑定完成ChannelFuture future = bootstrap.bind().sync();// 获取Channel的CloseFuture,并阻塞当前线程直到它完成future.channel().closeFuture().sync();} catch (Exception e) {log.error("Init netty server fail ", e);} finally {// 关闭EventLoopGroup,释放所有的资源group.shutdownGracefully().sync();}}
}

下面是服务器的主要代码组件:

  • EasyNettyServerHandler实现了业务逻辑
    
  • main()方法引导/初始化了服务器

引导过程所需要的步骤如下:

  • 创建一个ServerBootstrap的实例以引导和绑定服务器
  • 创建并分配一个NioEventLoopGroup实际以进行事件的处理,如接受新链接以及读、写数据
  • 指定服务器绑定的本地InetSocketAddress
  • 使用一个EasyNettyServerHandler的实例初始化一个新的Channel
  • 调用ServerBootstrap.bind()方法绑定服务器

做完以上步骤,服务器已经初始化就绪,能被使用了。

2.编写客户端

客户端将会:

  1. 连接到服务器
  2. 发送一个或者多个消息
  3. 对于每个消息,等到并接受从服务器发回的相同的消息
  4. 关闭连接

编写客户端所涉及的两个主要代码部分也是业务逻辑和引导。

2.1 通过ChannelHandler实现客户端逻辑

在客户端将使用ChannelInboundHandler,扩展SimpleChannelInboundHandler类以处理所有必须得任务。一般需要重写以下方法:

  • channelActive(),在到服务器的连接已经建立之后将调用
  • channelRead0(),当从服务器接收到一条消息时被调用
  • exceptionCaught(),在处理过程中引发异常时被调用
// 标记该类的实例可以被多个Channel共享
@ChannelHandler.Sharable
public class EasyNettyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// 当被通知Channel是活跃的时候,发送一条消息ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Netty!", CharsetUtil.UTF_8));}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {// 记录已接收消息的转储System.out.println("Client received: " + byteBuf.toString(CharsetUtil.UTF_8));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 在发生异常时,记录错误并关闭channel连接cause.printStackTrace();ctx.close();}
}

重写channelRead0()方法,即每当接收数据时,都会调用该方法。需要注意的是,由服务器发送的消息可能会被分块接收。如服务器发送了5字节,那么不能保证这5字节会被一次性接收。即便是这么少的数据,channelRead0()方法也可能被调用两次,第一次使用一个持有3字节的BuyeBuf,第二次使用一个持有2字节的ByteBuf。作为一个面向流的协议,TCP保证了字节数组将会按照服务器发送顺序去接收。

SimpleChannelInboundHandler与ChannelInboundHandler

这里的客户端为啥使用SimpleChannelInboundHandler而不是ChannelInboundHandler?

在客户端,当channelRead0()方法完成时,已经有了传入消息且处理完它了。当该方法返回时,SimpleChannelInboundHandler负责释放指向保存该消息的Bytebuf的内存引用。

在EasyNettyServerHandler中,还需要将传入消息回送给发送者,而write()操作是一部的,直到channelRead()方法返回后可能仍然没有完成。为此,EasyNettyServerHandler扩展了ChannelInboundHandlerAdapter,其在这个时间点上不会释放消息。

消息在EasyNettyServerHandler的channelReadComplete()方法中,当writeAndFluseh()方法被调用时释放。

2.2 引导客户端 

相关代码如下:


public class EasyNettyClient {public static final String HOST = "127.0.0.1";public static final int PORT = 8009;public static void main(String[] args) throws Exception {new EasyNettyClient().start();}public void start() throws Exception {NioEventLoopGroup group = new NioEventLoopGroup();try {// 创建BootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group)                   // 指定EventLoopGroup以处理客户端事件.channel(NioSocketChannel.class) // 需要适用于NIO的实现;适用于NIO传输的Channel类型.remoteAddress(new InetSocketAddress(HOST, PORT)) //设置服务器的InetSocketAddress.handler(new ChannelInitializer<SocketChannel>() { @Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EasyNettyClientHandler());}});// 连接到远程节点,阻塞等待直到连接完成ChannelFuture future = bootstrap.connect().sync();// 阻塞直到Channel关闭future.channel().closeFuture().sync();} finally {// 关闭线程池并且释放所有资源group.shutdownGracefully().sync();}}
}

客户端和服务端同时使用了NIO传输,可也可以在服务端和客户端分别使用不同的传输。如服务端使用NIO传输,客户端使用OIO传输。如何选择用于特定用例的特定传输的各种因素和场景在后面的系列会讲。

总结引导客户端的步骤:

  • 为初始化客户端,创建了一个Bootstrap实例
  • 为进行事件处理分配了一个NioEventLoopGroup实例,其中事件处理包括创建新链接以及处理入站和出站数据。
  • 为服务器连接创建了一个InetSocketAddress实例
  • 当连接被创建是,一个EasyNettyClientHandler实例将被安装到该channel对应的ChannelPipeline中
  • 在一切都设置完后,调用Bootstrap.connect()方法连接到远程节点。

以上步骤便完成客户端了。

3.运行程序

3.1 正常流程

先启动服务端,再启动客户端,服务端会收到“Hello Netty”的指令。接着服务器报告并接收到的消息,并将其传回客户端;客户端报告返回的消息并退出。

服务端

客户端

 

以上的行为是正常的行为。

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

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

相关文章

【CKA模拟题】查找集群中使用内存最高的node节点

题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetesFind the Node that consumes the most MEMORY in all cluster(currently we have single cluster). Then, store the result in the …

云效 AppStack + 阿里云 MSE 实现应用服务全链路灰度

作者&#xff1a;周静、吴宇奇、泮圣伟 在应用开发测试验证通过后、进行生产发布前&#xff0c;为了降低新版本发布带来的风险&#xff0c;期望能够先部署到灰度环境&#xff0c;用小部分业务流量进行全链路灰度验证&#xff0c;验证通过后再全量发布生产。本文主要介绍如何通…

springboot网站开发如何配置log4j日志插件

springboot网站开发如何配置log4j日志插件&#xff01;为了便于服务器等环境下的错误情况的排查根源&#xff0c;还是很有必要使用日志插件的&#xff0c;它可以记录下我们提前埋下的锚点信息。 在遇到故障&#xff0c;查看这些锚点记录的日志信息&#xff0c;可以快速高效的解…

低压MOS在无人机上的应用-REASUNOS瑞森半导体

一、前言 无人机的结构由机身、动力系统、飞行控制系统、链路系统、任务载荷等几个方面组成的。 无人机动力系统中的电机&#xff0c;俗称“马达”&#xff0c;是无人机的动力来源&#xff0c;无人机通过改变电机的转速来改变无人机的飞行状态。即改变每个电机的速度&#xf…

【Django开发】前后端分离美多商城项目第3篇:用户部分,1. 后端接口设计:【附代码文档】

美多商城项目4.0文档完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;美多商城&#xff0c;项目准备1.B2B--企业对企业,2.C2C--个人对个人,3.B2C--企业对个人,4.C2B--个人对企业。项目准备&#xff0c;配置1. 修改settings/dev.py 文件中的路径信息,2. INS…

数据结构--二叉树(超详细)

目录 1. 树概念及结构 1.1树概念 1.2树的表示 2. 二叉树概念及结构 2.1概念 2.2数据结构中的二叉树 2.3特殊的二叉树 2.4二叉树的存储结构 2.4.1顺序存储 2.4.2链式存储 2.5二叉树的性质 3. 二叉树顺序结构及概念 3.1二叉树的顺序结构 3.2堆的概念及结构 3.3堆的…

uinapp开发-PHP语言-后端安装说明-适用于圈子-陪玩-交友-校园-团购-外卖-分销等多系统-APP小程序H5多端皆有!

后端安装说明 全新安装客户&#xff0c;按此安装调试步骤&#xff0c;请按顺序&#xff1a; ** 后台安装步骤及说明 ** 1、在服务器里安装宝塔。下载www.bt.cn。 宝塔安装完毕后&#xff0c;安装环境&#xff0c;Nginx或者Apache 请选择PHP7.3 数据库mysql5.6。 NGINX 1.22.1轻…

matlab ICP配准高阶用法——统计每次迭代的配准误差并可视化

目录 一、概述二、代码实现三、结果展示1、原始点云2、配准结果3、配准误差本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 在进行论文写作时,需要做对比实验,来分析改进算法的性能,期间用到了迭代误差分布统…

前端框架推荐 Arco Design

Arco Design - 企业级产品的完整设计和开发解决方案 预览地址&#xff1a;Arco Design Pro - 开箱即用的中台前端/设计解决方案 一 开发 有vue3、React版本。 文档地址&#xff1a;Arco Design - 企业级产品的完整设计和开发解决方案 还配有对应脚手架&#xff1a;GitHub -…

【网络安全】CobaltStrike 使用

本文章仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若读者因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与作者无关。 Cobalt Strike是一款渗透测试神器&#xff0c;Cobalt Strike已经不再使用MSF而是作为单独的平台使…

ubuntu 22.04 安装驱动可能遇到的坑

1. 第一个坑 【gcc版本不匹配】 请确认你的gcc版本是不是12的&#xff0c;11是编译不通过的。 一般在最后编译时报错&#xff0c;在/var/log/nvidia-installer.log日志中可以看到以下字样 Warning: Compiler version check failed:The major and minor number of the compil…

Flink GateWay、HiveServer2 和 hive on spark

Flink SQL Gateway简介 从官网的资料可以知道Flink SQL Gateway是一个服务&#xff0c;这个服务支持多个客户端并发的从远程提交任务。Flink SQL Gateway使任务的提交、元数据的查询、在线数据分析变得更简单。 Flink SQL Gateway的架构如下图&#xff0c;它由插件化的Endpoi…

探秘开源隐语:架构深度剖析与隐私计算技术之旅

1.隐语架构 隐语&#xff08;SecretFlow&#xff09;作为蚂蚁集团开源的可信隐私计算框架&#xff0c;其架构设计具有多层次的特点&#xff0c;虽然具体分层名称可能会根据实际描述略有差异&#xff0c;但我们可以依据已有的技术和信息对其进行结构化的拆解&#xff1a; 硬件层…

如何使用Excel创建一个物品采购表

在企业的日常运营中&#xff0c;物品采购是一个常见且重要的活动。有效的采购管理不仅可以确保企业及时获得所需物资&#xff0c;还可以控制成本、提高效率。Microsoft Excel是一个功能强大的工具&#xff0c;它可以帮助我们创建和管理物品采购表。本文将详细介绍如何使用Excel…

Lua | 一篇文章讲清Lua语法及热更新

目录 一、环境搭建 二、Lua语法 1.输出print、单行注释、多行注释 2.变量 &#xff08;1&#xff09;nil &#xff08;2&#xff09;number &#xff08;3&#xff09;string &#xff08;3.1&#xff09;字符串长度 &#xff08;3.2&#xff09;字符串拼接 &#xf…

归并算法详细解析

归并排序 1945年&#xff0c;约翰冯诺依曼&#xff08;John von Neumann&#xff09;发明了归并排序&#xff0c;这是典型的分治算法的应用。归并排序&#xff08;Merge sort&#xff09;是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法&#xff08;Di…

数学建模(Topsis python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 案例&#xff1a; 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 标…

RequestResponse使用

文章目录 一、Request&Response介绍二、Request 继承体系三、Request 获取请求数据1、获取请求数据方法&#xff08;1&#xff09;、请求行&#xff08;2&#xff09;、请求头&#xff08;3&#xff09;、请求体 2、通过方式获取请求参数3、IDEA模板创建Servlet4、请求参数…

WEB 表单练习题

任务如图&#xff1a; <html><head><meta charest"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head><body><table width"…

Google的MELON: 通过未定位图像重建精确3D模型的突破性算法

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…