当前位置: 首页 > news >正文

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和编解码器&#xff08;一&#xff09;HTTP和SSL/TLS的添加和使用 一、SSL和TLS添加二、基于Netty的HTTP程序2.1 HTTP解码器、编码器和编解码器2.2 聚合HTTP消息2.3 HTTP压缩 一、SSL和TLS添加 作为一个通讯框架&#xff0c;通讯数据的安全性也是不可或缺的…...

Qos服务质量、心跳机制、保留消息,遗嘱信息,用户密码认证

这里写目录标题 Qos服务质量使用ESP8266接收QoS1的MQTT消息保留消息&#xff08;retainFlag&#xff09;心跳机制遗嘱信息 Qos服务质量 若想实现QoS>0&#xff0c;订阅端连接服务端时cleanSession需要设置为false&#xff0c;订阅端订阅主题时QoS>0&#xff0c;发布端发…...

MATLAB 之 线性方程组求解

这里写目录标题 一、线性方程组求解1. 线性方程组的直接解法1.1 利用左除运算符的直接解法1.2 利用矩阵的分解求解线性方程组 2. 线性方程组的迭代解法2.1 Jacobi 迭代法2.2 Gauss-Serdel 迭代法 3. 求线性方程的通解 一、线性方程组求解 在 MATLAB 中&#xff0c;关于线性方程…...

华为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 使用和注意事项说明&#xff1a; https://www.npmjs.com/package/taroj…...

【LeetCode】110. 平衡二叉树

110. 平衡二叉树&#xff08;简单&#xff09; 思路 对二叉树做先序遍历&#xff0c;从底至顶返回子树最大高度&#xff0c;若判定某子树不是平衡树则“剪枝”直接向上返回。 递归返回值&#xff1a; 当节点 root 左、右子树的高度差 > 1&#xff1a;返回 -1&#xff0c;代…...

SQL视图、存储过程、触发器

一、视图 &#xff08;一&#xff09;介绍 视图(view&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&…...

DNS隧道穿透

介绍&#xff1a; DNS隧道&#xff0c;是隧道技术中的一种。当我们的HTTP、HTTPS这样的上层协议、正反向端口转发都失败的时候&#xff0c;可以尝试使用DNS隧道。DNS隧道很难防范&#xff0c;因为平时的业务也好&#xff0c;使用也罢&#xff0c;难免会用到DNS协议进行解析&am…...

1.2 Scala变量与数据类型

一、变量声明 &#xff08;一&#xff09;简单说明 Scala中变量的声明使用关键字val和var。val类似Java中的final变量&#xff0c;也就是常量&#xff0c;一旦初始化将不可修改&#xff1b;var类似Java中的非final变量&#xff0c;可以被多次赋值&#xff0c;多次修改。 val - …...

深入探讨软件测试的质量度量指标

本文的目的是介绍项目中使用到主要质量指标&#xff0c;这些质量指标可以分为以下三类&#xff1a; 质量保证过程指标生产事故管理指标度量质量文化指标 质量保证过程指标 质量保证指标可以通过测试覆盖率来度量功能和非功能测试的覆盖率&#xff0c;同时也可以根据测试发现…...

6.12作业

1、pinia和vuex的区别 1.pinia没有mutations&#xff0c;只有state,getters,actions 2.pinia分模块不需要modules (之前vuex分模块需要modules) 3.pinia体积更小(性能更好) 4.pinia可以直接修改state数据 2、Vue2和vue3的响应式原理分别是什么&#x…...

RabbitMQ集群部署之镜像模式

RabbitMQ集群的普通模式中&#xff0c;一旦创建队列的主机宕机&#xff0c;队列就会不可用。不具备高可用能力。如果要解决这个问题&#xff0c;必须使用官方提供的镜像集群方案。 官方文档地址&#xff1a;https://www.rabbitmq.com/ha.html 1.镜像模式的特征 默认情况下&a…...

【算法】Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点

文章目录 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点问题描述&#xff1a;分析代码 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点 问题描述&#xff1a; 给你一个链表的头节点 head&am…...

音悦台项目测试报告

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

数据库存储过程和函数

MySQL存储过程和存储函数 MySQL中提供存储过程&#xff08;procedure&#xff09;与存储函数&#xff08;function&#xff09;机制&#xff0c;我们先将其统称为存储程序&#xff0c;一般的SQL语句需要先编译然后执行&#xff0c;存储程序是一组为了完成特定功能的SQL语句集&…...

Spring依赖注入有哪些?各有什么优缺点?

文章目录 前言概述一、属性注入1.1 实例1.2 优点1.3 缺点 二、Setter注入2.1 实例2.2 优点2.3 缺点 三、 构造方法注入3.1 实例3.2 优点3.3 缺点 四、扩展 前言 IoC和DI是Spring中重要的两个概念&#xff0c;其中IoC指的是控制反转&#xff0c;DI(依赖注入)指的是IoC的具体实现…...

java八股文-并发篇

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

Elasticsearch8.6.0安装

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

Vue - 第五天 动态组件 插槽 自定义指令

动态组件& 插槽& 自定义指令 一、动态组件1.什么是动态组件2.如何实现动态组件渲染3.使用 keep-alive 保持状态4. keep-alive 对应的生命周期函数5. keep-alive 的 include 属性6.动态展示左右组件7.例子 二、插槽1.什么是插槽2.体验插槽的基础用法2.1 没有预留插槽的内…...

如何开展web自动化测试

Web 自动化是指使用测试脚本在 Web 上自动执行任务。它包括填写表单、导航网页、单击链接或按钮以及从网站中提取数据等任务。 它可用于各种目的&#xff0c;例如自动输入数据或测试网站的功能。有几种工具和编程语言可用于自动化网络上的任务&#xff0c;包括Selenium&#x…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

字符串哈希+KMP

P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...