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

Netty的HTTP解码器、编码器和编解码器

HTTP是基于请求/响应模式的:客户端向服务器发送一个HTTP请求,然后服务器将会返回一个HTTP响应。Netty提供了多种编码器和解码器以简化对这个协议的使用。下面两图分别展示了生产和消费HTTP 请求和HTTP 响应的方法。

HTTP 请求的组成部分

HTTP 响应的组成部分

HTTP 解码器和编码器

HttpRequestEncoder 将HttpRequest、HttpContent 和LastHttpContent 消息编码为字节
HttpResponseEncoder 将HttpResponse、HttpContent 和LastHttpContent 消息编码为字节
HttpRequestDecoder 将字节解码为HttpRequest、HttpContent 和LastHttpContent 消息
HttpResponseDecoder 将字节解码为HttpResponse、HttpContent 和LastHttpContent 消息

添加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) {
pipeline.addLast("decoder", new HttpResponseDecoder());// 如果是客户端,则添加HttpResponseDecoder以处理来自服务器的响应
pipeline.addLast("encoder", new HttpRequestEncoder());// 如果是客户端,则添加HttpRequestEncoder以向服务器发送请求
} else {
pipeline.addLast("decoder", new HttpRequestDecoder());// 如果是服务器,则添加HttpRequestDecoder以接收来自客户端的请求
pipeline.addLast("encoder", new HttpResponseEncoder());// 如果是服务器,则添加HttpResponseEncoder以向客户端发送响应
}
}
}

聚合HTTP 消息

在ChannelInitializer将ChannelHandler安装到ChannelPipeline中之后,你便可以处理不同类型的HttpObject消息了。但是由于HTTP的请求和响应可能由许多部分组成,因此你需要聚合它们以形成完整的消息。为了消除这项繁琐的任务,Netty提供了一个聚合器,它可以将多个消息部分合并为FullHttpRequest或者FullHttpResponse 消息。通过这样的方式,你将总是看到完整的消息内容。
由于消息分段需要被缓冲,直到可以转发一个完整的消息给下一个ChannelInboundHandler,所以这个操作有轻微的开销。其所带来的好处便是你不必关心消息碎片了。

引入这种自动聚合机制只不过是向ChannelPipeline 中添加另外一个ChannelHandler罢了。下面代码展示了如何做到这点。

    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) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
}
pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));// 将最大的消息大小为512KB的HttpObjectAggregator添加到ChannelPipeline
}
}

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<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) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
pipeline.addLast("decompressor", new HttpContentDecompressor());// 如果是客户端,则添 加HttpContentDecompressor以处理来自服务器的压缩内容
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
pipeline.addLast("compressor", new HttpContentCompressor());// 如果是服务器,则添加HttpContentCompressor来压缩数据(如果客户端支持它)
}
}
}

版本注意点

如果你正在使用的是JDK 6 或者更早的版本,那么你需要将JZlib(www.jcraft.com/jzlib/)添加到CLASSPATH 中以支持压缩功能。
对于Maven,请添加以下依赖项:
<dependency>
  <groupId>com.jcraft</groupId>
  <artifactId>jzlib</artifactId>
  <version>1.1.3</version>
</depe ndency>

Netty中使用HTTPS

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

    public class HttpsCodecInitializer extends ChannelInitializer<Channel> {
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());
pipeline.addFirst("ssl", new SslHandler(engine));// 将SslHandler添加到ChannelPipeline中以使用HTTPS
if (isClient) {
pipeline.addLast("codec", new HttpClientCodec());// 如果是客户端,则添加HttpClientCodec
} else {
pipeline.addLast("codec", new HttpServerCodec());// 如果是服务器,则添加HttpServerCodec
}
}
}

前面的代码是一个很好的例子,说明了Netty的架构方式是如何将代码重用变为杠杆作用的。只需要简单地将一个ChannelHandler添加到ChannelPipeline中,便可以提供一项新功能,甚至像加密这样重要的功能都能提供。

Netty中使用WebSocket

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

WebSocket规范以及它的实现代表了对一种更加有效的解决方案的尝试。简单地说,WebSocket提供了“在一个单个的TCP连接上提供双向的通信……结合WebSocket API……它为网页和远程服务器之间的双向通信提供了一种替代HTTP轮询的方案。”

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

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

WebSocketFrame类型

BinaryWebSocketFrame 数据帧:二进制数据
TextWebSocketFrame 数据帧:文本数据
ContinuationWebSocketFrame 数据帧:属于上一个BinaryWebSocketFrame或者TextWebSocketFrame的文本的或者二进制数据
CloseWebSocketFrame 控制帧:一个CLOSE请求、关闭的状态码以及关闭的原因
PingWebSocketFrame 控制帧:请求一个PongWebSocketFrame
PongWebSocketFrame 控制帧:对PingWebSocketFrame请求的响应

因为Netty主要是一种服务器端的技术,所以在这里我们重点创建WebSocket服务器。以下代码清单展示了一个使用WebSocketServerProtocolHandler的简单示例,这个类处理协议升级握手,以及3种控制帧——Close、Ping和Pong。Text和Binary数据帧将会被传递给下一个(由你实现的)ChannelHandler进行处理。以下展示Netty在服务器端支持WebSocket

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

保护WebSocket

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

关于WebSocket的客户端示例,请参考Netty源代码中所包含的例子:https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/client。

构建基于Netty 的HTTP/HTTPS 应用程序的更多相关文章

  1. Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序

    通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...

  2. 基于Netty的IdleStateHandler实现Mqtt心跳

    基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...

  3. [WCF安全3]使用wsHttpBinding构建基于SSL与UserName授权的WCF应用程序

    上一篇文章中介绍了如何使用wsHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建基于SSL的UserName安全授权的WCF应用程序. ...

  4. Netty学习——基于netty实现简单的客户端聊天小程序

    Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...

  5. 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)

    本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...

  6. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

  7. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  8. 基于Netty的私有协议栈的开发

    基于Netty的私有协议栈的开发 书是人类进步的阶梯,每读一本书都使自己得以提升,以前看书都是看了就看了,当时感觉受益匪浅,时间一长就又还回到书本了!所以说,好记性不如烂笔头,以后每次看完一本书都写一 ...

  9. ASP.NET Web Api构建基于REST风格的服务实战系列教程

    使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...

随机推荐

  1. 【一天一道LeetCode】#21. Merge Two Sorted Lists

    一天一道LeetCode系列 (一)题目 Merge two sorted linked lists and return it as a new list. The new list should ...

  2. Ext JS添加子组件的误区

    经常会有人问我,为什么我的Grid不能岁窗口的变得而自动调整.了解后,发现很多人都习惯在渲染子组件的时候将Gird渲染到容器内的一个div里,而这正是问题的所在. 在Ext JS的布局系统中,能控制到 ...

  3. 超过1个G免费资源,16套质量超高风格多样的移动UIKIT

    编者按:前两天发了一篇价值4000元的收费可商用Web 模版,今天来一波同样高质量的的App UI KIT,包括音乐/餐厅/运动等等类型的App,无论是下载来学习还是商用(对的可商用!)都不容错过,@ ...

  4. RecyclerView notifyItem闪烁的问题

    之前我们做点赞,用listview做的话,就是在item实现点击后,写一个scal动画,不过现在都转到RecyclerView,那么要做这种效果于是做了一个notifyItemChanged()的操作 ...

  5. Android中使用SVG矢量图(一)

    SVG矢量图介绍 首先要解释下什么是矢量图像,什么是位图图像? 1.矢量图像:SVG (Scalable Vector Graphics, 可伸缩矢量图形) 是W3C 推出的一种开放标准的文本式矢量图 ...

  6. x64系统的判断和x64下文件和注册表访问的重定向——补记

    原来的地址 x64系统的判断和x64下文件和注册表访问的重定向(1) x64系统的判断和x64下文件和注册表访问的重定向(2) x64系统的判断和x64下文件和注册表访问的重定向(3) 之前在(3)里 ...

  7. MTK平台 Android4.0.3 定制关机动画

    实现效果是这样的,长按电源键弹出关机对话框,选择关机项将呈现关机动画和音乐直到正常关机完毕,下面说说具体思路及实现代码 找到长按电源键控制代码 /frameworks/base/policy/src/ ...

  8. XMPP系列(三)---获取好友列表、添加好友

    1.心跳检测.掉线重连功能 客户端和服务器端都可以设置多久发送一次心跳包,如果对方没有返回正确的pong信息,则会断开连接,而添加掉线重连功能,则会自动进行连接. 如果自己写聊天功能还得自己做心跳检测 ...

  9. Gathering Initial Troubleshooting Information for Analysis of ORA-4031 Errors on the Shared Pool

    In this Document   Purpose   Troubleshooting Steps   References APPLIES TO: Oracle Database - Enterp ...

  10. Linux内核中断和异常分析(中)

    在linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,上次讲到单片机的时候,我就讲到了单片机 ...