博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netty——预置的ChannelHandler和编解码器(一)
阅读量:2444 次
发布时间:2019-05-10

本文共 8976 字,大约阅读时间需要 29 分钟。

预置的ChannelHandler和编解码器

Netty 为许多通用协议提供了编解码器和处理器,几乎可以开箱即用,这减少了你在那些相 当繁琐的事务上本来会花费的时间与精力。在本章中,我们将探讨这些工具以及它们所带来的好 处,其中包括 Netty 对于 SSL/TLS 和 WebSocket 的支持,以及如何简单地通过数据压缩来压榨 HTTP,以获取更好的性能。

通过SSL/TLS保护Netty应用程序

为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLContext 和 SSLEngine 类使得实现解密和加密相当简单直接。Netty 通过一个名为 SslHandler 的 ChannelHandler 实现利用了这个 API,其中 SslHandler 在内部使用 SSLEngine 来完成实际的工作。

在这里插入图片描述

示例:

使用 ChannelInitializer 来将 SslHandler 添加到 Channel- Pipeline 中。

public class SslChannelInitializer extends ChannelInitializer
{
private final SslContext context; private final boolean startTls; public SslChannelInitializer(SslContext context, boolean startTls) {
//传入要使用的SslContext this.context = context; //如果设置为true,第一个写入的消息不会被加密(客户端应该设置为true) 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将是ChannelPipeline中的第一个ChannelHandler。 这确保了只有在所有其他的 ChannelHandler 将它们的逻辑应用到数据之后,才会进行加密。

SslHandler 具有一些有用的方法,如下表所示。例如,在握手阶段,两个节点将相互 验证并且商定一种加密方式。你可以通过配置 SslHandler 来修改它的行为,或者在 SSL/TLS 握手一旦完成之后提供通知,握手阶段完成之后,所有的数据都将会被加密。SSL/TLS 握手将会 被自动执行。

在这里插入图片描述

Netty 的 OpenSSL/SSLEngine 实现:

Netty 还提供了使用 OpenSSL 工具包(www.openssl.org)的 SSLEngine 实现。这个 OpenSslEngine 类提供了比 JDK 提供的 SSLEngine 实现更好的性能。

如果 OpenSSL 库可用,可以将 Netty 应用程序(客户端和服务器)配置为默认使用 OpenSslEngine。

如果不可用,Netty 将会回退到 JDK 实现。有关配置 OpenSSL 支持的详细说明,参见 Netty 文档: http://netty.io/wiki/forked-tomcat-native.html#wikih2-1

注意,无论你使用 JDK 的 SSLEngine 还是使用 Netty 的 OpenSslEngine,SSL API 和数据流都是一致的。

构建基于Netty的Http/Https应用程序

HTTP/HTTPS 是最常见的协议套件之一,并且随着智能手机的成功,它的应用也日益广泛, 因为对于任何公司来说,拥有一个可以被移动设备访问的网站几乎是必须的。这些协议也被用于 其他方面。许多组织导出的用于和他们的商业合作伙伴通信的 WebService API 一般也是基于 HTTP(S)的。

HTTP解码器、编码器和编解码器

HTTP 是基于请求/响应模式的:客户端向服务器发送一个 HTTP 请求,然后服务器将会返回 一个 HTTP 响应。Netty 提供了多种编码器和解码器以简化对这个协议的使用。

HTTP请求:

在这里插入图片描述
HTTP响应:
在这里插入图片描述

一个 HTTP 请求/响应可能由多个数据部分组成,并且它总是以一 个 LastHttpContent 部分作为结束。FullHttpRequest 和 FullHttpResponse 消息是特 殊的子类型,分别代表了完整的请求和响应。所有类型的 HTTP 消息都实现了 HttpObject 接口。

HTTP解码器和编码器:

在这里插入图片描述

示例:添加HTTP支持

public class HttpPipelineInitailizer extends ChannelInitializer
{
private final boolean client; public HttpPipelineInitailizer(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 {
//如果是服务器,则添加 HttpRequestDecoder 以接收来自客户端的请求 pipeline.addLast("decoder", new HttpResponseDecoder()); //如果是服务器,则添加 HttpResponseEncoder 以向客户端发送响应 pipeline.addLast("encoder", new HttpResponseEncoder()); } }}

聚合HTTP消息

在 ChannelInitializer 将 ChannelHandler 安装到 ChannelPipeline 中之后,你 便可以处理不同类型的 HttpObject 消息了。但是由于 HTTP 的请求和响应可能由许多部分组 成,因此你需要聚合它们以形成完整的消息。为了消除这项繁琐的任务,Netty 提供了一个聚合 器,它可以将多个消息部分合并为 FullHttpRequest 或者 FullHttpResponse 消息。通过 这样的方式,你将总是看到完整的消息内容。

由于消息分段需要被缓冲,直到可以转发一个完整的消息给下一个 ChannelInboundHandler,所以这个操作有轻微的开销。其所带来的好处便是你不必关心消息碎片了。

引入这种自动聚合机制只不过是向 ChannelPipeline 中添加另外一个 ChannelHandler 罢了。

//自动聚合HTTP的消息片段public class HttpAggregatorInitalizer extends ChannelInitializer
{
private final boolean isClient; public HttpAggregatorInitalizer(boolean isClient) {
this.isClient = isClient; } @Override protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); //如果是客户端,则添加HttpClientCodec if (isClient) {
pipeline.addLast("codec", new HttpClientCodec()); } else {
//如果是服务器,则添加HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); } //将最大的消息大小为512KB的HttpObjectAggregator添加到ChannelPipeline pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024)); }}

HTTP压缩

当使用 HTTP 时,建议开启压缩功能以尽可能多地减小传输数据的大小。虽然压缩会带来一 些 CPU 时钟周期上的开销,但是通常来说它都是一个好主意,特别是对于文本数据来说。

Netty 为压缩和解压缩提供了 ChannelHandler 实现,它们同时支持 gzip 和 deflate 编 码。

HTTP 请求的头部信息 客户端可以通过提供以下头部信息来指示服务器它所支持的压缩格式:GET /encrypted-area HTTP/1.1 Host: www.example.com Accept-Encoding: gzip, deflate然而,需要注意的是,服务器没有义务压缩它所发送的数据。
//自动压缩HTTP消息public class HttpCompressionInitializer extends ChannelInitializer
{
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 HttpContentCompressor()); } else {
// 如果是服务器,则添 加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); // 如果是服务器,则添加 HttpContentCompressor 来压缩数据(如果客户端支持它) pipeline.addLast("compressor", new HttpContentCompressor()); } }}如果你正在使用的是 JDK 6 或者更早的版本,那么你需要将 JZlib(www.jcraft.com/jzlib/)添加到 CLASSPATH 中以支持压缩功能。
com.jcraft
jzlib
1.1.3

使用HTTPS

启用 HTTPS 只需要将 SslHandler 添加到 ChannelPipeline 的 ChannelHandler 组合中:

public class HttpsCodecInitializer extends ChannelInitializer
{
private final SslContext context; private final boolean isClient; public HttpsCodecInitializer(SslContext context, boolean isClient) {
this.context = context; this.isClient = isClient; } @Override protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); SSLEngine engine = context.newEngine(ch.alloc()); //将 SslHandler 添加到 ChannelPipeline 中以 使用 HTTPS pipeline.addFirst("ssl", new SslHandler(engine)); if (isClient) {
pipeline.addLast("codec", new HttpClientCodec()); } else {
pipeline.addLast("codec", new HttpServerCodec()); } }}

WebSocket

WebSocket解决了一个长期存在的问题:既然底层的协议(HTTP)是一个请求/响应模式的 交互序列,那么如何实时地发布信息呢?AJAX提供了一定程度上的改善,但是数据流仍然是由客户端所发送的请求驱动的。还有其他的一些或多或少的取巧方式 ,但是最终它们仍然属于扩展性受限的变通之法。 WebSocket规范以及它的实现代表了对一种更加有效的解决方案的尝试。

简单地说,WebSocket提供了“在一个单个的TCP连接上提供双向的通信......结合WebSocket API......它为网 页和远程服务器之间的双向通信提供了一种替代HTTP轮询的方案。

也就是说,WebSocket 在客户端和服务器之间提供了真正的双向数据交换。我们不会深入地 描述太多的内部细节,但是我们还是应该提到,尽管最早的实现仅限于文本数据,但是现在已经 不是问题了;WebSocket 现在可以用于传输任意类型的数据,很像普通的套接字。

通信将作为普通的 HTTP 协议 开始,随后升级到双向的 WebSocket 协议:

在这里插入图片描述

要想向你的应用程序中添加对于 WebSocket 的支持,你需要将适当的客户端或者服务器 WebSocketChannelHandler 添加到 ChannelPipeline 中。这个类将处理由 WebSocket 定义 的称为帧的特殊消息类型。

如下表所示,WebSocketFrame 可以被归类为数据帧或者控制帧。

在这里插入图片描述

示例:

因为Netty主要是一种服务器端的技术,所以在这里我们重点创建WebSocket服务器 。

下面代码展示了一个使用WebSocketServerProtocolHandler的简单示例,这个类处理协议升级握手,以及 3 种控制帧——Close、Ping和Pong。Text和Binary数据帧将会被传递给 下一个(由你实现的)ChannelHandler进行处理。

public class WebSocketServerInitializer extends ChannelInitializer
{
@Override protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast( new HttpServerCodec(), // 为握手提供聚合的HttpRequest new HttpObjectAggregator(65535), // 如果被请求 的端点是 "/websocket", 则处理该 升级握手 new WebSocketServerProtocolHandler("/websocket"), // TextFrameHandler 处理 TextWebSocketFrame new TextFrameHandler(), new BinaryFrameHandler(), // ContinuationFrameHandler 处理 ContinuationWebSocketFrame new ContinuationFrameHandler()); } public static final class TextFrameHandler extends SimpleChannelInboundHandler
{
@Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
//Handler text frame } } public static final class BinaryFrameHandler extends SimpleChannelInboundHandler
{
@Override protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
//Handle binary frame } } public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler
{
@Override protected void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {
//Handle continuation frame } }}

要想为 WebSocket 添加安全性,只需要将 SslHandler 作为第一个 ChannelHandler 添加到

ChannelPipeline 中。

转载地址:http://elpqb.baihongyu.com/

你可能感兴趣的文章
Ubuntu 18.04 LTS现在在Microsoft Store中
查看>>
如何检查已安装的Ubuntu版本
查看>>
如何在Windows 10上禁用附近共享
查看>>
gmail_Gmail将提供自毁电子邮件
查看>>
google 禁止广告_是否应禁止针对个人的广告?
查看>>
Plover.io在本地设备之间快速共享文件
查看>>
如何在OS X照片中禁用iCloud照片同步
查看>>
Minecraft的官方网站分发了受恶意软件感染的皮肤
查看>>
如何组织您的iPhone主屏幕
查看>>
如何使用Odin手动更新三星手机
查看>>
word模板快速填内容_如何快速轻松地在Word中选择内容块
查看>>
如何在Word 2013中直接从一个表导航到另一个表
查看>>
twitch 录像_如何通过NVIDIA GeForce Experience将您的PC游戏玩法传送到Twitch
查看>>
linux gnome_在Gnome中学习这些鼠标技巧,以获得更高效的Linux体验
查看>>
打印机疑难解答_使用内置电源疑难解答改善Windows 7中的电池寿命
查看>>
下载spotify音乐_完成播放列表或专辑后如何停止Spotify停止自动播放音乐
查看>>
nest keyword_PSA:如果您的Nest Cam没有启用2FA,则黑客可能会监视您
查看>>
drupal加密_立即更新您的Drupal网站,否则黑客可能将其变成加密货币矿工
查看>>
vimrc配置 鼠标光标_在“提示”框中:即时调整窗口大小,包含鼠标光标并了解电池配置...
查看>>
询问HTG:安装XBMC附加组件,缩小视频以进行移动播放,自动更改默认打印机
查看>>