构建基于Netty 的HTTP/HTTPS 应用程序
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 应用程序的更多相关文章
- Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序
通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...
- 基于Netty的IdleStateHandler实现Mqtt心跳
基于Netty的IdleStateHandler实现Mqtt心跳 IdleStateHandler解析 最近研究jetlinks编写的基于Netty的mqtt-client(https://githu ...
- [WCF安全3]使用wsHttpBinding构建基于SSL与UserName授权的WCF应用程序
上一篇文章中介绍了如何使用wsHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建基于SSL的UserName安全授权的WCF应用程序. ...
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)
本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...
- 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server
本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...
- 基于Netty打造RPC服务器设计经验谈
自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...
- 基于Netty的私有协议栈的开发
基于Netty的私有协议栈的开发 书是人类进步的阶梯,每读一本书都使自己得以提升,以前看书都是看了就看了,当时感觉受益匪浅,时间一长就又还回到书本了!所以说,好记性不如烂笔头,以后每次看完一本书都写一 ...
- ASP.NET Web Api构建基于REST风格的服务实战系列教程
使用ASP.NET Web Api构建基于REST风格的服务实战系列教程[十]——使用CacheCow和ETag缓存资源 系列导航地址http://www.cnblogs.com/fzrain/p/3 ...
随机推荐
- Android Studio下使用NDK的流程
我要重新拿回持之以恒徽章!! 老规矩,先说看能学会什么:ANDROID STUDIO下NDK的使用方法.JNI的基本使用方法,C语言调用JAVA的方法. 首先要下载NDK,如果你没有VPN可以来htt ...
- android采用SurfaceView实现文字滚动效果
前言 为了实现文字的滚动效果,之前也重写了TextView效果都不太好,后来对SurfaceView进行完善. 声明 欢迎转载,但请保留文章原始出处:) 小崔博客:http://blog.c ...
- 使用SVM对多类多维数据进行分类
最近,本人要做个小东西,使用SVM对8类三维数据进行分类,搜索网上,发现大伙讨论的都是二维数据的二分类问题,遂决定自己研究一番.本人首先参考了opencv的tutorial,这也是二维数据的二分类问题 ...
- PS 图像调整算法——自动对比度 (Auto Contrast)
PS 给出的定义: Enhance Monochromatic Contrast: Clips all channels identically. This preserves the overall ...
- 数据包接收系列 — IP协议处理流程(二)
本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog 我们接着来看数据包如何发往本地的四层协议. ip_local_del ...
- Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!
Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...
- obj-c编程19:关联对象
对于一些无法子类化的实例对象来说,如果希望将一个对象与其绑定该如何做呢? 以下示例虚构了一个HyConsoleAlert类,User类将会使用该类在控制台显示定制的告警.如果User中包括多个Aler ...
- masm中list文件和宏的一些常用编译调试查看方法
我们知道使用用 ml /Fl a.asm 可以生成lst文件,但是如果不加调整,masm默认生成的lst文件是非常大的,因为它包含了很大的windows必须用到的头文件内容,为了减小lst文件大小,便 ...
- 恶补web之一:html学习(2)
iframe用于在网页内显示网页:<iframe src="URL"></iframe>,iframe可用作链接的目标: <!DOCTYPE html ...
- 页面加载完之前显示Loading
1.第一种方式 HTML <body class="is-loading"> <div class="curtain"> <div ...