Netty实战(十一)
预置的ChannelHandler和编解码器(一)HTTP和SSL/TLS的添加和使用
- 一、SSL和TLS添加
- 二、基于Netty的HTTP程序
- 2.1 HTTP解码器、编码器和编解码器
- 2.2 聚合HTTP消息
- 2.3 HTTP压缩
一、SSL和TLS添加
作为一个通讯框架,通讯数据的安全性也是不可或缺的一部分。一般常见的像TLS/SSL这样的安全协议我们都应该熟悉。 我们在访问安全网站时都遇到过这些协议,但是它们也可用于其他不是基于HTTP的应用程序,如安全SMTP(SMTPS)邮件服务器甚至是关系型数据库系统。
像Java就提供了javax.net.ssl 包来支持SSL/TLS,它的 SSLContext 和 SSLEngine类使得实现解密和加密相当简单直接。Netty 则是通过一个名为 SslHandler 的 ChannelHandler实现利用了这个 API,其中 SslHandler 在内部使用 SSLEngine 来完成实际的工作。
Netty 的 OpenSSL/SSLEngine 实现:
Netty 还提供了使用 OpenSSL工具包(www.openssl.org)的 SSLEngine 实现。这个 OpenSslEngine 类提供了比 JDK 提供的SSLEngine 实现更好的性能。如果OpenSSL库可用,可以将Netty应用程序(客户端和服务器)配置为默认使用OpenSslEngine。 如果不可用,Netty将会回退到 JDK 实现。 注意,无论你使用 JDK 的 SSLEngine 还是使用 Netty 的 OpenSslEngine,SSL API 和数据流都 是一致的。
下面这张图展示了SslHandler 进行解密和加密数据流:
下面我们使用ChannelInitializer来将SslHandler添加到ChannelPipeline 中,ChannelInitializer在channel注册好时设置ChannelPipeline 我们之前说过,忘记可以看看前面的内容。
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;import javax.net.ssl.SSLEngine;/*** Author: lhd* Data: 2023/6/11* Annotate: Netty 添加SSL/TLS支持*/
public class SslChannelInitializer extends ChannelInitializer<Channel> {private final SslContext context;private final boolean startTls;//传入要使用的SslContext,startTls如果设置为 true,第一个写入的消息将不会被加密(客户端应该设置为 true)public SslChannelInitializer(SslContext context, boolean startTls) {this.context = context;this.startTls = startTls;}@Overrideprotected void initChannel(Channel ch) throws Exception {//对于每个 SslHandler 实例,都使用 Channel 的 ByteBufAllocator 从 SslContext 获取一个新的 SSLEngineSSLEngine engine = context.newEngine(ch.alloc());//将 SslHandler 作为第一个ChannelHandler 添加到 ChannelPipeline 中ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls));}
}
SslHandler同样包含其他的方法,例如:在握手阶段,两个节点将相互验证并且商定一种加密方式。我们可以配置 SslHandler 来修改它的行为,或者在 SSL/TLS握手一旦完成之后提供通知,握手阶段完成之后,所有的数据都将会被加密。SSL/TLS 握手将会被自动执行。
下面是一些SslHandler的方法:
方 法 名 称 | 描 述 |
---|---|
setHandshakeTimeout (long,TimeUnit);setHandshakeTimeoutMillis (long);getHandshakeTimeoutMillis() | 设置和获取超时时间,超时之后,握手ChannelFuture 将会被通知失败 |
setCloseNotifyTimeout (long,TimeUnit);setCloseNotifyTimeoutMillis (long);getCloseNotifyTimeoutMillis() | 设置和获取超时时间,超时之后,将会触发一个关闭通知并关闭连接。这也将会导致通知该 ChannelFuture 失败 |
handshakeFuture() | 返回一个在握手完成后将会得到通知的ChannelFuture。如果握手先前已经执行过了,则返回一个包含了先前的握手结果的 ChannelFuture |
close();close(ChannelPromise);close(ChannelHandlerContext,ChannelPromise) | 发送 close_notify 以请求关闭并销毁 |
底层的 SslEngine |
二、基于Netty的HTTP程序
HTTP/HTTPS大部分同学都不会陌生,它是我们常用的协议之一。我们熟悉的另一个协议 WebService API 一般也是基于HTTP/HTTPS的。
下面我们使用Netty 提供的 ChannelHandler,来处理 HTTP 和 HTTPS协议。
2.1 HTTP解码器、编码器和编解码器
HTTP 是基于请求/响应模式的:客户端向服务器发送一个 HTTP 请求,然后服务器将会返回一个 HTTP 响应。Netty 提供了多种编码器和解码器简化了对这个协议的使用。
我们先来看看如生产和消费HTTP请求以及HTTP响应的方法:
- 下图是一个HTTP的请求的组成:
- 下图是HTTP响应的组成:
一个 HTTP 请求/响应可能由多个数据部分组成,并且它总是以一个 LastHttpContent部分作为结束。FullHttpRequest 和 FullHttpResponse 消息是特殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息都实现了 HttpObject 接口。
那么HTTP的编码器和解码器都包含哪些方法呢?
名 称 | 描 述 |
---|---|
HttpRequestEncoder | 将HttpRequest、HttpContent 和 LastHttpContent 消息编码为字节 |
HttpResponseEncoder | 将HttpResponse、HttpContent 和LastHttpContent 消息编码为字节 |
HttpRequestDecoder | 将字节解码为HttpRequest、HttpContent 和 LastHttpContent 消息 |
HttpResponseDecoder | 将字节解码为HttpResponse、HttpContent 和LastHttpContent 消息 |
了解了HTTP协议后,我们将它添加到我们的应用程序中:
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;/*** Author: lhd* Data: 2023/6/11* Annotate: 添加HTTP协议支持*/
public class HttpPipelineInitializer extends ChannelInitializer<Channel> {private final boolean client;public HttpPipelineInitializer(boolean client) {this.client = client;}@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();if (client) {//如果是客户端,则添加HttpResponseDecoder 以处理来自服务器的响应pipeline.addLast("decoder", new HttpResponseDecoder());//如果是客户端,则添加 HttpRequestEncoder以向服务器发送请求pipeline.addLast("encoder", new HttpRequestEncoder());} else {//如果是服务器,则添加 HttpResponseEncoder以向客户端发送响应pipeline.addLast("decoder", new HttpRequestDecoder());//如果是服务器,则添加 HttpRequestDecoder以接收来自客户端的请求pipeline.addLast("encoder", new HttpResponseEncoder());}}
}
2.2 聚合HTTP消息
为什么要聚合HTTP消息?
ChannelInitializer 将 ChannelHandler 安装到 ChannelPipeline中之后,便可以处理不同类型的 HttpObject 消息了。但是由于 HTTP
的请求和响应可能由许多部分组成,因此需要聚合它们以形成完整的消息。
Neey是如何做的?
Netty 提供了一个聚合器,它可以将多个消息部分合并为 FullHttpRequest 或者 FullHttpResponse 消息。通过这样的方式,我们将看到完整的消息内容。由于消息分段需要被缓冲,直到可以转发一个完整的消息给下一个 ChannelInboundHandler,所以这个操作有轻微的开销。
下面展示一下这种操作是如何进行的:
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;/*** Author: lhd* Data: 2023/6/11* Annotate: HTTP消息聚合*/
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {private final boolean isClient;public HttpAggregatorInitializer(boolean isClient) {this.isClient = isClient;}@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();if (isClient) {//如果是客户端,则添加 HttpClientCodecpipeline.addLast("codec", new HttpClientCodec());} else {//如果是服务器,则添加 HttpServerCodecpipeline.addLast("codec", new HttpServerCodec());}//将最大的消息大小为 512 KB的 HttpObjectAggregator 添加到 ChannelPipelinepipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));}
}
2.3 HTTP压缩
HTTP为什么要压缩?不是已经聚合了么?
当使用 HTTP 时,压缩可以尽可能的多地减小传输数据的大小。虽然压缩会带来一些 CPU时钟周期上的开销,但是通常来说它都是一个好主意,特别是对于文本数据来说。
Netty 为压缩和解压缩提供了 ChannelHandler 实现,它们同时支持 gzip 和 deflate 编 码。
HTTP 请求的头部信息
客户端可以通过提供以下头部信息来指示服务器它所支持的压缩格式:
GET /encrypted-area HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, deflate
然而,需要注意的是,服务器没有义务压缩它所发送的数据。
下面展示一下如何自动的压缩HTTP消息:
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpServerCodec;/*** Author: lhd* Data: 2023/6/11* Annotate: HTTP消息压缩*/
public class HttpCompressionInitializer extends ChannelInitializer<Channel> {private final boolean isClient;public HttpCompressionInitializer(boolean isClient) {this.isClient = isClient;}@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();if (isClient) {//如果是客户端,则添加 HttpClientCodecpipeline.addLast("codec", new HttpClientCodec());//如果是客户端,则添加HttpContentDecompressor 以处理来自服务器的压缩内容pipeline.addLast("decompressor", new HttpContentDecompressor());} else {//如果是服务器,则添加 HttpServerCodecpipeline.addLast("codec", new HttpServerCodec());//如果是服务器,则添加HttpContentCompressor来压缩数据(如果客户端支持它)pipeline.addLast("compressor", new HttpContentCompressor());}}
}
压缩及其依赖
如果你正在使用的是 JDK 6 或者更早的版本,那么你需要将 JZlib(www.jcraft.com/jzlib/)添加到CLASSPATH 中以支持压缩功能。
对于 Maven,请添加以下依赖项:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>
<version>1.1.3</version>
</dependency>
相关文章:

Netty实战(十一)
预置的ChannelHandler和编解码器(一)HTTP和SSL/TLS的添加和使用 一、SSL和TLS添加二、基于Netty的HTTP程序2.1 HTTP解码器、编码器和编解码器2.2 聚合HTTP消息2.3 HTTP压缩 一、SSL和TLS添加 作为一个通讯框架,通讯数据的安全性也是不可或缺的…...
Qos服务质量、心跳机制、保留消息,遗嘱信息,用户密码认证
这里写目录标题 Qos服务质量使用ESP8266接收QoS1的MQTT消息保留消息(retainFlag)心跳机制遗嘱信息 Qos服务质量 若想实现QoS>0,订阅端连接服务端时cleanSession需要设置为false,订阅端订阅主题时QoS>0,发布端发…...
MATLAB 之 线性方程组求解
这里写目录标题 一、线性方程组求解1. 线性方程组的直接解法1.1 利用左除运算符的直接解法1.2 利用矩阵的分解求解线性方程组 2. 线性方程组的迭代解法2.1 Jacobi 迭代法2.2 Gauss-Serdel 迭代法 3. 求线性方程的通解 一、线性方程组求解 在 MATLAB 中,关于线性方程…...

华为OD机试真题 Java 实现【字符串序列判定】【2022Q4 100分】,附详细解题思路
一、题目描述 输入两个字符串a和b,都只包含英文小写字母。a长度<=100,b长度<=500,000。 判定a是否是b的有效子串。 判定规则: a中的每个字符在b中都能找到(可以不连续),且a在b中字符的前后顺序与a中顺序要保持一致。 (例如,a=”qwt”是b=”qwerty”的一个子…...

taro使用小记 —— 持续更新
目录 1、在 taro 中使用 axios2、在 taro 中添加全局组件自动引入和方法自动引入3、在 taro 中使用 pinia 1、在 taro 中使用 axios taro 3.6 版本已经支持了网络请求库。 需安装插件 tarojs/plugin-http 使用和注意事项说明: https://www.npmjs.com/package/taroj…...

【LeetCode】110. 平衡二叉树
110. 平衡二叉树(简单) 思路 对二叉树做先序遍历,从底至顶返回子树最大高度,若判定某子树不是平衡树则“剪枝”直接向上返回。 递归返回值: 当节点 root 左、右子树的高度差 > 1:返回 -1,代…...
SQL视图、存储过程、触发器
一、视图 (一)介绍 视图(view)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑&…...

DNS隧道穿透
介绍: DNS隧道,是隧道技术中的一种。当我们的HTTP、HTTPS这样的上层协议、正反向端口转发都失败的时候,可以尝试使用DNS隧道。DNS隧道很难防范,因为平时的业务也好,使用也罢,难免会用到DNS协议进行解析&am…...
1.2 Scala变量与数据类型
一、变量声明 (一)简单说明 Scala中变量的声明使用关键字val和var。val类似Java中的final变量,也就是常量,一旦初始化将不可修改;var类似Java中的非final变量,可以被多次赋值,多次修改。 val - …...

深入探讨软件测试的质量度量指标
本文的目的是介绍项目中使用到主要质量指标,这些质量指标可以分为以下三类: 质量保证过程指标生产事故管理指标度量质量文化指标 质量保证过程指标 质量保证指标可以通过测试覆盖率来度量功能和非功能测试的覆盖率,同时也可以根据测试发现…...
6.12作业
1、pinia和vuex的区别 1.pinia没有mutations,只有state,getters,actions 2.pinia分模块不需要modules (之前vuex分模块需要modules) 3.pinia体积更小(性能更好) 4.pinia可以直接修改state数据 2、Vue2和vue3的响应式原理分别是什么&#x…...

RabbitMQ集群部署之镜像模式
RabbitMQ集群的普通模式中,一旦创建队列的主机宕机,队列就会不可用。不具备高可用能力。如果要解决这个问题,必须使用官方提供的镜像集群方案。 官方文档地址:https://www.rabbitmq.com/ha.html 1.镜像模式的特征 默认情况下&a…...
【算法】Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点
文章目录 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点问题描述:分析代码 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点 问题描述: 给你一个链表的头节点 head&am…...

音悦台项目测试报告
文章目录 项目背景项目功能测试计划与设计功能测试自动化测试 测试结果功能测试结果UI自动化测试结果 项目背景 现如今人们的生活压力大,容易使人疲惫,为了使得人们在闲暇之余可以听音乐放松,为此设计出一款轻量的听音乐网站,快速…...

数据库存储过程和函数
MySQL存储过程和存储函数 MySQL中提供存储过程(procedure)与存储函数(function)机制,我们先将其统称为存储程序,一般的SQL语句需要先编译然后执行,存储程序是一组为了完成特定功能的SQL语句集&…...
Spring依赖注入有哪些?各有什么优缺点?
文章目录 前言概述一、属性注入1.1 实例1.2 优点1.3 缺点 二、Setter注入2.1 实例2.2 优点2.3 缺点 三、 构造方法注入3.1 实例3.2 优点3.3 缺点 四、扩展 前言 IoC和DI是Spring中重要的两个概念,其中IoC指的是控制反转,DI(依赖注入)指的是IoC的具体实现…...

java八股文-并发篇
并发篇 1. 线程状态 要求 掌握 Java 线程六种状态掌握 Java 线程状态转换能理解五种状态与六种状态两种说法的区别 六种状态及转换 分别是 新建 当一个线程对象被创建,但还未调用 start 方法时处于新建状态此时未与操作系统底层线程关联 可运行 调用了 start …...

Elasticsearch8.6.0安装
Elasticsearch 8.5.0 安装 Elasticsearch 简介Elasticsearch 8.6.0 安装创建网络拉取镜像运行镜像设置密码修改kibana配置绑定ES代码绑定:手动绑定: 配置ik分词器扩展词词典停用词词典 Elasticsearch 简介 Elasticsearch(ES) 是一…...

Vue - 第五天 动态组件 插槽 自定义指令
动态组件& 插槽& 自定义指令 一、动态组件1.什么是动态组件2.如何实现动态组件渲染3.使用 keep-alive 保持状态4. keep-alive 对应的生命周期函数5. keep-alive 的 include 属性6.动态展示左右组件7.例子 二、插槽1.什么是插槽2.体验插槽的基础用法2.1 没有预留插槽的内…...
如何开展web自动化测试
Web 自动化是指使用测试脚本在 Web 上自动执行任务。它包括填写表单、导航网页、单击链接或按钮以及从网站中提取数据等任务。 它可用于各种目的,例如自动输入数据或测试网站的功能。有几种工具和编程语言可用于自动化网络上的任务,包括Selenium&#x…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...