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 …

Linux实战笔记(四) 后台运行

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲 Linux 系统怎么在后台运行命令 0、序言 很多时候&#xff0c;特别是在连接服务器进行开发时&#xff0c;通常会遇到以下问题&#xff1a; 运行一些命令时&#xff0c;终端被阻塞&#xff0c;无法执行其他操作运行一些命令…

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

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

大数据安全分析相关与安全分析的场景

数据采集与预处理 数据源是大数据分析的基础和前提&#xff0c;进行安全分析需要收集的数据源&#xff1a; 日志数据&#xff1a;设备与系统的日志和安全告警信息流量数据&#xff1a;网络流量数据、包括netflow数据和全流量镜像数据支持数据&#xff1a;资产信息、账号信息、漏…

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

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

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

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

Uni App中遇到的跨域问题

通过与多年开发经验的h5前端沟通后&#xff0c;可以对某些事情更加明确&#xff0c;才此期间&#xff0c;会和后端、运维同学产生密不可分的交集&#xff0c;成了谁来改的问题。 明确&#xff1a;跨域99%由后台来配置&#xff0c;如果在本地开发&#xff0c;可以手动配置&…

VB.NET 中的委托(Delegate)是什么?它们的作用是什么?

VB.NET 中的委托&#xff08;Delegate&#xff09;是什么&#xff1f;它们的作用是什么&#xff1f; 在VB.NET中&#xff0c;委托&#xff08;Delegate&#xff09;是一种类型&#xff0c;它可以用来引用方法。委托可以看作是函数指针的一种类型安全版本&#xff0c;在编译时提…

【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堆的…

【Golang星辰图】数据管理利器:Go编程语言中的数据库和搜索引擎综合指南

高效数据处理&#xff1a;Go编程语言中的数据库和全文搜索引擎详细介绍 前言 Go编程语言是一种强大、类型安全且高效的编程语言&#xff0c;它在处理数据库和搜索引擎方面有着广泛的应用。本篇文章将详细介绍几个Go编程语言中常用的数据库和全文搜索引擎&#xff0c;包括Go-b…

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而是作为单独的平台使…

后端学习笔记

一、spring源码相关 1、Spring常规Bean创建过程 Spring常规Bean创建过程 2、spring启动注册过程 spring启动注册过程 二、docker相关 1、安装docker 安装docker 2、安装Rancher 安装Rancher 3、安装Harbor 安装Harbor 4、安装Mysql 安装Mysql 5、安装ElasticSear…

AR VR技术

虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术在当今科技领域中备受瞩目&#xff0c;它们已经成为了主流技术&#xff0c;并在各个领域得到了广泛的应用。以下是当前主流的VR和AR技术&#xff1a; 1. 虚拟现实&#xff08;VR&#xff09;技术&…

IOS面试题编程机制 6-10

6. 如何理解MVVM设计模式?MVVM即 Model-View-ViewModel 1.View主要用于界面呈现,与用户输入设备进行交互 2.ViewModel是MVVM架构中最重要的部分,ViewModel中包含属性,方法,事件,属性验证等逻辑,负责View与Model之间的通讯 3.Model就是我们常说的数据模型,用于数据的构造…

谷歌浏览器(Google Chrome) 常用快捷键和扩展程序

谷歌浏览器常用的快捷键 Google Chrome Mac快捷键 在 Mac 操作系统中使用 Google Chrome 浏览器时&#xff0c;大多数快捷键的 Ctrl 键被替换为 Command 键&#xff08;⌘&#xff09;。以下是一些常用的快捷键&#xff1a; 窗口和标签页管理&#xff1a; ⌘N&#xff1a;打开…

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…