Netty实战(十一)

news2024/11/19 13:27:56

预置的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;
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        //对于每个 SslHandler 实例,都使用 Channel 的 ByteBufAllocator 从 SslContext 获取一个新的 SSLEngine
        SSLEngine 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;
    }
    @Override
    protected 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;
    }
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            //如果是客户端,则添加 HttpClientCodec
            pipeline.addLast("codec", new HttpClientCodec());
        } else {
            //如果是服务器,则添加 HttpServerCodec
            pipeline.addLast("codec", new HttpServerCodec());
        }
        //将最大的消息大小为 512 KB的 HttpObjectAggregator 添加到 ChannelPipeline
        pipeline.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;
    }
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (isClient) {
            //如果是客户端,则添加 HttpClientCodec
            pipeline.addLast("codec", new HttpClientCodec());
            //如果是客户端,则添加HttpContentDecompressor 以处理来自服务器的压缩内容
            pipeline.addLast("decompressor", new HttpContentDecompressor());
        } else {
            //如果是服务器,则添加 HttpServerCodec
            pipeline.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>

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

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

相关文章

LVS+KeepAlived集群

LVSKeepAlived集群 一.KeepAlived的原理 1.1基于什么协议 KeepAlived基于VRRP热备份协议# VRRP协议号112# VRRP组播地址224.0.0.18# VRRP通告报文的TTL值必须是2551.2如何选举Master 1&#xff09;初始化时根据state判断master和backup。 2&#xff09;最终根据优先级决定m…

【小沐学Python】Python实现在线电子书(Sphinx + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建测试工程4、项目文件结构5、编译为本地文件6、编译为http服务7、更改样式主题8、支持markdown9、修改文档显示结构10、项目托管到github11、部署到ReadtheDocs结语 1、简介 Sphinx 是一个 文档生成器 &#xff0c;您也可以把它看成一种工具&…

Win10开启混合现实模拟器

最近要做一个类似工业元宇宙的项目&#xff0c;准备先在Win10上先进行模拟&#xff0c;而Win10已经提供了混合现实模拟器&#xff0c;可以在没有头显的情况下进行模拟。本文讲解如何开启这个模拟器。 微软官方给了一个链接讲述如何开启混合现实模拟器&#xff0c;可以点击这里…

嘀嗒陪诊小程序v1.0.8+小程序前端

嘀嗒陪诊小程序功能相对简单&#xff0c;后台也简捷&#xff0c;如果只是做个陪诊服务的小程序也基本能满足了&#xff0c;整体测试了下海参崴发现BUG&#xff0c;小程序端也能正常为使用&#xff0c;唯一用户授权接口是老的。 应用背景&#xff1a;人口老龄化少子化&#xff…

【数据结构】--单链表力扣面试题⑦环形链表

注&#xff1a;本篇文章不含环形链表的数学推理证明&#xff0c;只提供图解等思路 环形链表是一个非常经典的问题 题述&#xff1a;给定一个链表&#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续追踪 next 指针再次到达&#xff0c;则链表中…

代码随想录算法训练营第五十三天|1143.最长公共子序列|1035.不相交的线|53. 最大子序和

LeetCode1143.最长公共子序列 动态规划五部曲&#xff1a; 1&#xff0c;确定dp数组&#xff08;dp table&#xff09;以及下标的含义&#xff1a;dp[i][j]&#xff1a;长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]。为什么要定…

【无功优化】“碳中和”目标下电气互联系统有功-无功协同优化模型(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

从零玩转系列之微信支付实战基础框架搭建

一、前言 halo各位大佬很久没更新了最近在搞微信支付,因商户号审核了我半个月和小程序认证也找了资料并且将商户号和小程序进行关联,至此微信支付Native支付完成.此篇文章过长我将分几个阶段的文章发布(项目源码都有,小程序和PC端) 在此之前已经更新了微信支付开篇、微信支付安…

【Matlab代码实现】电动过滤器:LPF和HPF、模拟调制:调幅和调频、WiFi、蓝牙和蜂窝网络的容量分析.....

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

第五章 二次型

引言 题型总结中推荐例题有蓝皮书的题型较为重要&#xff0c;只有吉米多维奇的题型次之。码字不易&#xff0c;如果这篇文章对您有帮助的话&#xff0c;希望您能点赞、评论、收藏&#xff0c;投币、转发、关注。您的鼓励就是我前进的动力&#xff01; 知识点思维导图 补充&…

十一、进程间通信——管道

目录 零、前置知识 一、什么是进程间通信 &#xff08;一&#xff09;含义 &#xff08;二&#xff09;发展 &#xff08;三&#xff09;类型 1.管道 2.System V IPC 3.POSIX IPC 二、为什么要有进程间通信 三、怎么进行进程间通信 &#xff08;一&#xff09;什么是…

PoseiSwap的趋势性如何体现?

DEX 代表了一种先进的意识形态&#xff0c;相对于 CEX 其更强调无许可、去中心化以及公开透明。然而随着 DeFi 赛道逐渐从 2021 年年底的高峰逐渐转向低谷&#xff0c;DEX 整体的交易量、TVL等数据指标也开始呈现下滑的趋势&#xff0c;DEX 正在面临发展的新瓶颈期。 在这样的背…

时间序列预测的20个基本概念总结

1、时间序列 时间序列是一组按时间顺序排列的数据点 比如&#xff1a; 每小时的气压每年的医院急诊按分钟计算的股票价格 2、时间序列的组成部分 时间序列数据有三个主要组成部分。 趋势季节性残差或白噪声 3、趋势 在时间序列中记录的长期缓慢变化/方向。 4、季节性 …

51、基于51单片机洗衣机控制系统(带水位)系统设计(程序+原理图+PCB源文件+Proteus仿真+参考论文+开题报告+任务书+流程图+元器件清单等)

摘 要 随着数字技术的快速发展&#xff0c;数字技术被广泛应用于智能控制的领域中。单片机以体积小、功能全、价格低廉、开发方便的优势得到了许多电子系统设计者的青睐。它适合于实时控制&#xff0c;可构成工业控制器、智能仪表、智能接口、智能武器装置以及通用测控单元等。…

【最优PID 整定】PID性能指标(ISE,IAE,ITSE和ITAE)优化、稳定性裕量(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

ChatGPT1论文解读《Improving Language Understanding by Generative Pre-Training》(2018)

论文总结 以下是我阅读完整篇论文做的个人总结&#xff0c;基本包含了ChatGPT1设计的完整框架思路&#xff0c;可以仅看【论文总结】章节。 在GPT1实现的核心架构中&#xff0c;包含两个阶段。 第一阶段 在第一阶段基于一个包含7000本书籍内容的海量未标注文本数据集进行无…

PLC模拟量超限报警功能块

模拟量偏差报警功能块请参看下面文章: 模拟量偏差报警功能块(SCL代码)_RXXW_Dor的博客-CSDN博客工业模拟量采集的相关基础知识,可以查看专栏的系列文章,这里不再赘述,常用链接如下:PLC模拟量采集算法数学基础(线性传感器)_plc傳感器數據轉化_RXXW_Dor的博客-CSDN博客。…

GOOGLE | COT(chain of thought)开山之作,利用思维链提升复杂问题推理能力

一、概述 title&#xff1a;Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 论文地址&#xff1a;https://arxiv.org/abs/2201.11903 auto COT代码【COT升级版本】&#xff1a;GitHub - amazon-science/auto-cot: Official implementation for &qu…

【LeetCode】HOT 100(8)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

字符串--字符串字面量、字符串的存储

一、字符串字面量&#xff08;string literal&#xff09; 字符串字面量&#xff08;string literal &#xff09;&#xff0c;有时也成为字符串常量&#xff0c;它是由一对双引号括起来的一个字符序列。如“hello”&#xff0c;“123”等都是字符串。无论双引号内是否包含字符…