Netty In Action中文版 – 第十二章:SPDY
本章我将不会直接翻译Netty In Action书中的原文,感觉原书中本章讲的很多废话,我翻译起来也吃力。所以,本章内容我会根据其他资料和个人理解来讲述。
12.1 SPDY概念及背景
SPDY是Google开发的基于传输控制协议(TCP)的应用层协议,开发组正在推动SPDY成为正式标准(现为互联网草案).SPDY协议旨在通过压缩,多路复用和优先级来缩短网页的加载时间和提高安全性。(SPDY是Speedy的昵音,意思是更快)。
为什么需要SPDY?SPDY协议只是在性能上对HTTP做了很大的优化,其核心思想是尽量减少连接个数,而对于HTTP的语义并没有做太大的修改。具体来说是,SPDY使用了HTTP的方法和页眉,但是删除了一些头并重写了HTTP中管理连接和数据转移格式的部分,所以基本上兼容HTTP的。
Google在SPDY白皮书里表示要协议栈下面渗透并替换掉传输层协议(TCP),但是因为这样无论是部署起来还是实现起来暂时相当困难,因此Google准备先对应用层协议HTTP进行改进,先在SSL之上增加一个会话层来实现SPDY协议,而HTTP的GET和POST消息格式保持不变,即现有的所有服务端应用均不用做任何修改。因此在目前,SPDY的目的是为了加强HTTP,是对HTTP一个更好的实现和支持。至于未来SPDY得到广泛应用后会不会演一出狸猫换太子,替换掉HTTP并彻底颠覆整个Internet就是Google的事情了。
距离万维网之父蒂姆·伯纳斯 – 李发明并推动HTTP成为如今互联网最流行的协议已经过去十几年了(现用HTTP 1.1规范也停滞了13年了),随着现在WEB技术的飞速发展尤其是HTML5的不断演进,包括WebSockets协议的出现以及当前网络环境的改变,传输内容的变化,当初的HTTP规范已经逐渐无法满足人们的需要了,HTTP需要进一步发展,因此HTTPbis工作组已经被组织并被授权考虑HTTP 2.0,希望能解决掉目前HTTP所带来的诸多限制。而SPDY正是Google在HTTP即将从1.1跨越到2.0之际推出的试图成为下一代互联网通信的协议,长期以来一直被认为是HTTP 2.0唯一可行选择。
** SPDY相比HTTP有如下优点:**
- SPDY多路复用,请求优化;而HTTP单路连接,请求低效
- SPDY支持服务器推送技术;而HTTP只允许由客户端主动发起请求
- SPDY压缩了HTTP头信息,节省了传输数据的带宽流量;而HTTP头冗余,同一个会话会反复送头信息
- SPDY强制使用SSL传输协议,全部请求SSL加密后,信息传输更安全
谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%。
** 支持SPDY协议的浏览器:**
- Google Chrome 19+和Chromium 19+
- Mozilla Firefox 11+,从13开始默认支持
- Opera 12.10+
- Internet Explorer 11+
12.2本例子流程图
12.3 Netty中使用SPDY
支持SPDY的的ChannelPipeline如下图:
不支持SPDY的的ChannelPipeline如下图:
** 例子代码如下:**
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
import java.util.Arrays;
1.
import java.util.Collections;
1.
import java.util.List;
1.
1.
import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
1.
1.
公共
类 DefaultServerProvider
实现 ServerProvider {
1.
private
static
final List <String> PROTOCOLS = Collections.unmodifiableList(Arrays
- .asList(
“spdy / 3.1” ,
“http / 1.1” ,
“http / 1.0” ,
“未知” ));
1.
私人 字符串协议;
1.
public String getSelectedProtocol(){
返回 协议;
- }
@覆盖
public
void protocolSelected(String arg0){
这个.protocol = arg0;
- }
@覆盖
public List <String> protocols(){
返回 PROTOCOLS;
- }
@覆盖
public
void unsupported(){
- protocol =
“http / 1.1” ;
- }
- }
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
import io.netty.channel.ChannelFuture;
1.
import io.netty.channel.ChannelFutureListener;
1.
import io.netty.channel.ChannelHandlerContext;
1.
import io.netty.channel.SimpleChannelInboundHandler;
1.
import io.netty.handler.codec.http.DefaultFullHttpResponse;
1.
import io.netty.handler.codec.http.FullHttpRequest;
1.
import io.netty.handler.codec.http.FullHttpResponse;
1.
import io.netty.handler.codec.http.HttpHeaders;
1.
import io.netty.handler.codec.http.HttpResponseStatus;
1.
import io.netty.handler.codec.http.HttpVersion;
1.
import io.netty.util.CharsetUtil;
1.
1.
公共
类 HttpRequestHandler
扩展 SimpleChannelInboundHandler <FullHttpRequest> {
1.
@覆盖
protected
void channelRead0(ChannelHandlerContext ctx,FullHttpRequest请求)
抛出 异常{
如果 (HttpHeaders.is100ContinueExpected(request)){
- send100Continue(CTX);
- }
- FullHttpResponse响应=
新的 DefaultFullHttpResponse(
- request.getProtocolVersion(),HttpResponseStatus.OK);
- 。response.content()writeBytes(的getContent()的getBytes(CharsetUtil.UTF_8));
- response.headers()。组(HttpHeaders.Names.CONTENT_TYPE,
“text / plain; charset = UTF-8” );
boolean keepAlive = HttpHeaders.isKeepAlive(request);
if (keepAlive){
- response.headers()。组(HttpHeaders.Names.CONTENT_LENGTH,
- 。response.content()readableBytes());
- response.headers()。组(HttpHeaders.Names.CONNECTION,
- HttpHeaders.Values.KEEP_ALIVE);
- }
- ChannelFuture future = ctx.writeAndFlush(response);
如果 (!keepAlive){
- future.addListener(ChannelFutureListener.CLOSE);
- }
- }
private
static
void send100Continue(ChannelHandlerContext ctx){
- FullHttpResponse响应=
新的 DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
- HttpResponseStatus.CONTINUE);
- ctx.writeAndFlush(响应);
- }
protected String getContent(){
返回
“此内容通过HTTP \ r \ n传输” ;
- }
@覆盖
public
void exceptionCaught(ChannelHandlerContext ctx,Throwable cause)
抛出 异常{
- cause.printStackTrace();
- ctx.close();
- }
- }
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
公共
类 SpdyRequestHandler
扩展 HttpRequestHandler {
1.
@覆盖
protected String getContent(){
返回
“此内容通过SPDY \ r \ n传输” ;
- }
- }
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
import io.netty.channel.ChannelInboundHandler;
1.
import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
1.
1.
import javax.net.ssl.SSLEngine;
1.
1.
import org.eclipse.jetty.npn.NextProtoNego;
1.
1.
public
class DefaultSpdyOrHttpChooser
extends SpdyOrHttpChooser {
1.
保护 DefaultSpdyOrHttpChooser(
int maxSpdyContentLength,
int maxHttpContentLength){
super (maxSpdyContentLength,maxHttpContentLength);
- }
@覆盖
保护 SelectedProtocol getProtocol(SSLEngine引擎){
- DefaultServerProvider提供程序=(DefaultServerProvider)NextProtoNego
- 获得(发动机);
- String protocol = provider.getSelectedProtocol();
if (protocol ==
null ){
返回 SelectedProtocol.UNKNOWN;
- }
开关 (协议){
案例
“spdy / 3.1” :
返回 SelectedProtocol.SPDY_3_1;
案例
“http / 1.0” :
案例
“http / 1.1” :
返回 SelectedProtocol.HTTP_1_1;
默认值:
返回 SelectedProtocol.UNKNOWN;
- }
- }
@覆盖
受保护的 ChannelInboundHandler createHttpRequestHandlerForHttp(){
返回
新的 HttpRequestHandler();
- }
@覆盖
受保护的 ChannelInboundHandler createHttpRequestHandlerForSpdy(){
返回
新的 SpdyRequestHandler();
- }
- }
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
import io.netty.channel.Channel;
1.
import io.netty.channel.ChannelInitializer;
1.
import io.netty.channel.ChannelPipeline;
1.
import io.netty.handler.ssl.SslHandler;
1.
1.
import javax.net.ssl.SSLContext;
1.
import javax.net.ssl.SSLEngine;
1.
1.
import org.eclipse.jetty.npn.NextProtoNego;
1.
1.
公共
类 SpdyChannelInitializer
扩展 ChannelInitializer <Channel> {
私人
最终 SSLContext上下文;
1.
公共 SpdyChannelInitializer(SSLContext上下文){
this .context = context;
- }
@覆盖
protected
void initChannel(Channel ch)
引发 Exception {
- ChannelPipeline pipeline = ch.pipeline();
- SSLEngine engine = context.createSSLEngine();
- engine.setUseClientMode(
false );
- NextProtoNego.put(引擎,
新的 DefaultServerProvider());
- NextProtoNego.debug =
true ;
- pipeline.addLast(
“sslHandler” ,
新的 SslHandler(引擎));
- pipeline.addLast(
“选择器” ,
新 DefaultSpdyOrHttpChooser(
1024 *
1024 ,
1024 *
1024 ));
- }
- }
**[java] **查看纯
文本
package netty.in.action.spdy;
1.
1.
import io.netty.bootstrap.ServerBootstrap;
1.
import io.netty.channel.Channel;
1.
import io.netty.channel.ChannelFuture;
1.
import io.netty.channel.nio.NioEventLoopGroup;
1.
import io.netty.channel.socket.nio.NioServerSocketChannel;
1.
import io.netty.example.securechat.SecureChatSslContextFactory;
1.
1.
import java.net.InetSocketAddress;
1.
1.
import javax.net.ssl.SSLContext;
1.
1.
公共
类 SpdyServer {
1.
private
final NioEventLoopGroup group =
new NioEventLoopGroup();
私人
最终 SSLContext上下文;
私人 频道频道;
1.
公共 SpdyServer(SSLContext上下文){
this .context = context;
- }
公共 ChannelFuture开始(InetSocketAddress地址){
- ServerBootstrap bootstrap =
new ServerBootstrap();
- bootstrap.group(组).channel(NioServerSocketChannel。
类)
- .childHandler(
新的 SpdyChannelInitializer(上下文));
- ChannelFuture future = bootstrap.bind(address);
- future.syncUninterruptibly();
- channel = future.channel();
回报 未来;
- }
public
void destroy(){
if (channel!=
null ){
- channel.close();
- }
- group.shutdownGracefully();
- }
public
static
void main(String [] args){
- SSLContext context = SecureChatSslContextFactory.getServerContext();
最终 SpdyServer端点=
新的 SpdyServer(上下文);
- ChannelFuture future = endpoint.start(
new InetSocketAddress(
4096 ));
- Runtime.getRuntime()。addShutdownHook(
new Thread(){
@覆盖
public
void run(){
- endpoint.destroy();
- }
- });
- 。future.channel()closeFuture()syncUninterruptibly();
- }
- }
使用SSL需要使用到的SSLContext,下面代买是获取的SSLContext对象:
**[java] **查看纯
文本
- / *
- *版权所有2012 The Netty项目
- *
- * Netty项目根据Apache许可证将此文件授权给您,
- *版本2.0(“许可证”); 除遵守规定外,您不得使用此文件
- *与许可证。您可以在以下地址获得许可证副本:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- *除非适用法律要求或书面同意,软件
- *根据许可证分发是按“原样”基础分发的,没有
- *任何种类的保证或条件,无论是明示还是暗示。看到了
- *用于管理权限和限制的特定语言的许可证
- *根据许可证。
- * /
package netty.in.action.spdy;
1.
1.
import javax.net.ssl.ManagerFactoryParameters;
1.
import javax.net.ssl.TrustManager;
1.
import javax.net.ssl.TrustManagerFactorySpi;
1.
import javax.net.ssl.X509TrustManager;
1.
import java.security.InvalidAlgorithmParameterException;
1.
import java.security.KeyStore;
1.
import java.security.KeyStoreException;
1.
import java.security.cert.X509Certificate;
1.
- / **
- *接受任何证书的虚假{TrustManagerFactorySpi}
- *即使它无效。
- * /
公共
类 SecureChatTrustManagerFactory
扩展 TrustManagerFactorySpi {
1.
private
static
final TrustManager DUMMY_TRUST_MANAGER =
new X509TrustManager(){
@覆盖
public X509Certificate [] getAcceptedIssuers(){
返回
新的 X509Certificate [
0 ];
- }
@覆盖
public
void checkClientTrusted(X509Certificate [] chain,String authType){
//永远相信 – 这是一个例子。
//你应该在现实世界中做点什么。
//只有启用了客户端证书身份验证后,您才会到达此处,
//如SecureChatSslContextFactory中所述。
- 通信System.err.println(
“UNKNOWN CLIENT CERTIFICATE:” + chain [
0 ] .getSubjectDN());
- }
@覆盖
public
void checkServerTrusted(X509Certificate [] chain,String authType){
//永远相信 – 这是一个例子。
//你应该在现实世界中做点什么。
- 通信System.err.println(
“UNKNOWN SERVER CERTIFICATE:” + chain [
0 ] .getSubjectDN());
- }
- };
public
static TrustManager [] getTrustManagers(){
返回
新的 TrustManager [] {DUMMY_TRUST_MANAGER};
- }
@覆盖
受保护的 TrustManager [] engineGetTrustManagers(){
返回 getTrustManagers();
- }
@覆盖
protected
void engineInit(KeyStore keystore)
抛出 KeyStoreException {
// 没用过
- }
@覆盖
protected
void engineInit(ManagerFactoryParameters managerFactoryParameters)
抛出 InvalidAlgorithmParameterException {
// 没用过
- }
- }
**[java] **查看纯
文本
- / *
- *版权所有2012 The Netty项目
- *
- * Netty项目根据Apache许可证将此文件授权给您,
- *版本2.0(“许可证”); 除遵守规定外,您不得使用此文件
- *与许可证。您可以在以下地址获得许可证副本:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- *除非适用法律要求或书面同意,软件
- *根据许可证分发是按“原样”基础分发的,没有
- *任何种类的保证或条件,无论是明示还是暗示。看到了
- *用于管理权限和限制的特定语言的许可证
- *根据许可证。
- * /
package netty.in.action.spdy;
1.
1.
import java.io.ByteArrayInputStream;
1.
import java.io.InputStream;
1.
- / **
- *提供所有必需信息的虚假密钥存储区
- *创建一个示例SSL连接。
- *
- *要生成一个伪造的密钥存储:
- * <pre>
- * keytool -genkey -alias securechat -keysize 2048 -validity 36500
- * -keyalg RSA -dname“CN = securechat”
- * – 关键秘密 – 通道秘密
- * -keystore cert.jks
- * </ pre>
- * /
公共
最终
课 SecureChatKeyStore {
private
static
final
short [] DATA = {
0xFE的,
0xed ,
0xFE时,
0xed ,
0×00 ,
0×00 ,
0×00 ,
0×02 ,
0×00 ,
0×00 ,
0×00 ,
0×02 ,
0×00 ,
0×00 ,
0×00 ,
0×01 ,
为0x00 ,
0x07的,
0x65 ,
0x78 ,
0x61 ,
0x6d ,
0x70 ,
0x6c ,
0x65 ,
0×00 ,
0×00 ,
0×01 ,
0X1A ,
0x9f ,
0×57 ,
0xA5的,
0×27 ,
0×00 ,
0×00 ,
0×01 ,
为0x9A ,
的0x30 ,
为0x82 ,
0×01 ,
0x96 ,
的0x30 ,
0x0E的,
0×06 ,
0X0A ,
0x2B访问,
0×06 ,
0×01 ,
0×04 ,
0×01 ,
0x2a ,
0×02 ,
0×11 ,
0×01 ,
0×01 ,
0×05 ,
为0x00 ,
0x04访问,
为0x82 ,
0×01 ,
为0x82 ,
0x48 ,
0x6d ,
0xcf ,
0x16 ,
0xB5执行,
为0x50 ,
位0x95 ,
0x36 ,
为0xBF ,
0X47 ,
0×27 ,
为0x50 ,
将0x58 ,
0X0D ,
0xa2 ,
0×52 ,
0x7e格式,
0x25 ,
是0xAB ,
0×14 ,
0X1A ,
值为0x26 ,
0x5e ,
0x2d ,
0x8a ,
0×23 ,
0×90 ,
0X60 ,
0x7f的,
0×12 ,
为0x20 ,
0x56储存,
0xd1 ,
0x43中,
0xa2 ,
0x6b ,
0X47 ,
0x5d ,
0xed ,
0x9d ,
0xd4 ,
为0xE5 ,
0×83 ,
0×28 ,
0x89上,
为0xC2 ,
0x16 ,
0x4c ,
0x76 ,
0×06 ,
写入0xAD ,
为0x8E ,
0x8c ,
0x29 ,
0X1A ,
0x9b ,
为0x0F ,
0xdd ,
0x60的,
0x4b ,
0xb4 ,
0X62 ,
为0x82 ,
0x9e ,
0x4a ,
0x63 ,
0×83 ,
0x2E之间,
0xd2 ,
0x43中,
0x78 ,
为0xC2 ,
0x32 ,
0x1F的,
0x60的,
0xa9 ,
0x8a ,
0x7f的,
为0x0F ,
0x7c ,
0xa6 ,
0x1d ,
0xe6 ,
0x92 ,
0x9e ,
0×52 ,
0xc7 ,
0x7d ,
为0xBB ,
0x35 ,
0x3b ,
和0xAA ,
0x89上,
0x73 ,
0x4c ,
0xFB的才能,
0x99 ,
0x54 ,
0x97 ,
0x99 ,
0×28 ,
0x6e ,
0x66 ,
0x5b ,
0xf7 ,
0x9b ,
0x7e格式,
0x6d ,
0x8a ,
值为0x2F ,
0xFA回应,
0xc3 ,
0X1E ,
0x71 ,
0xb9 ,
0xbd ,
值为0x8F ,
0xc5 ,
0x63 ,
0x25 ,
0X31 ,
0×20 ,
0×02 ,
0xFF的,
0×02 ,
0XF0 ,
0xc9 ,
0x2c上,
0xdd ,
0x3a ,
为0x10 ,
的0x30 ,
是0xAB ,
为0xE5 ,
0xAD,将,
0x3D之间,
0X1A ,
为0x82 ,
0x77 ,
0×46 ,
0xed ,
×03 ,
0x38 ,
0xa4 ,
0x73 ,
0x6d ,
0x36 ,
0x36 ,
0x33 ,
0x70 ,
0xb2 ,
0x63 ,
0x20的,
0xca ,
×03 ,
为0xBF ,
0x5a ,
0xF4中,
0x7c ,
0x35 ,
0XF0 ,
0x63 ,
0X1A ,
0×12 ,
0x33 ,
0×12 ,
将0x58 ,
0xd9 ,
0xa2 ,
0x63 ,
0x6b ,
0x63 ,
为0x82 ,
×41 ,
0x65 ,
0x70 ,
0x37符号,
0x4b ,
0x99 ,
0×04 ,
0x9f ,
0xdd ,
0x5e ,
0×07 ,
0×01 ,
位0x95 ,
0x9f ,
0x36 ,
0xe8 ,
0xc3 ,
0x66 ,
0x2a ,
为0x21 ,
0×69 ,
0x68 ,
0x40的,
0xe6 ,
0xbc ,
为0xBB ,
0x85 ,
0×81 ,
0×21 ,
0×13 ,
0xe6 ,
0xa4 ,
0xcf ,
0xd3 ,
0×67 ,
0xe3 ,
0xfd ,
0x75 ,
0XF0 ,
0xdf ,
0×83 ,
0xe0的,
0xc5 ,
0x36 ,
×09 ,
0xac ,
0x1b ,
0xd4 ,
0xf7 ,
0x2a ,
0×23 ,
×57 ,
为0x1C ,
0x5c ,
为0x0F ,
0xF4中,
0xcf ,
0xa2 ,
0xcf ,
0xf5 ,
0xbd ,
为0x9c ,
0×69 ,
0x98在全局,
0x78 ,
0x3a ,
0x25 ,
0xe4 ,
0xfd ,
0x85 ,
为0x11 ,
含有0xCC ,
0x7d ,
0xef ,
将0xEB ,
0x74 ,
0x60的,
0xb1 ,
0xb7 ,
0xFB的才能,
为0x1F ,
0x0E的,
0X62 ,
为0xFF ,
0xFE时,
×09 ,
0X0A ,
0xc3 ,
0x80的,
值为0x2F ,
0x10的,
×49 ,
0x89上,
0x78 ,
0xd2 ,
0×08 ,
0xFA回应,
0x89上,
为0x22 ,
0×45 ,
0x91 ,
为0x21 ,
0xbc ,
0×90 ,
0x3E的,
写入0xAD ,
0xb3 ,
0X0A ,
0xb4 ,
为0x0E ,
为0x1c ,
0xa1 ,
0x93 ,
0x92 ,
0xd8 ,
0x72 ,
0x07的,
0x54 ,
0X60 ,
0xe7 ,
0x91 ,
0xFC有,
0xd9 ,
为0x3C ,
0xe1 ,
0x6f ,
0×08 ,
0xe4 ,
0x56储存,
0xf6 ,
0x0B中,
0XB0 ,
为0x3C ,
0x39 ,
0x8a ,
0x2d ,
0x48 ,
0x44进行,
0×28 ,
0×13 ,
0xca ,
0xe9 ,
0xf7 ,
0xA3执行,
0xb6 ,
0x8a ,
0x5F的,
0X31 ,
0xa9 ,
0x72 ,
0xf2 ,
写0xDE ,
0x96 ,
0xf2 ,
0xb1 ,
0x53 ,
0xb1 ,
0x3E的,
0X24 ,
0×57 ,
0xfd ,
为0x18 ,
0×45 ,
为0x1F ,
0xc5 ,
0x33 ,
0x1b ,
0xa4 ,
0xe8 ,
为0x21 ,
0xFA回应,
为0x0E ,
0xb2 ,
0xb9 ,
0xcb ,
0xc7 ,
0x07的,
×41 ,
0xdd ,
值为0x2F ,
0xb6 ,
的0x6A ,
0×23 ,
为0x18 ,
0xed ,
0xc1 ,
0xef ,
0xe2 ,
0x4b ,
0xec ,
0xc9 ,
0xba ,
0xFB的才能,
0×46 ,
×43 ,
0×90 ,
0xd7 ,
0xB5执行,
0x68 ,
0×28 ,
0X31 ,
0x2B访问,
0x8d ,
0xa8 ,
0x51 ,
0x63 ,
0xf7 ,
0x53 ,
0x99 ,
0x19 ,
0x68 ,
0x85 ,
0x66 ,
0×00 ,
0×00 ,
0×00 ,
0×01 ,
0×00 ,
0×05 ,
将0x58 ,
0x2E之间,
0x35 ,
的0x30 ,
0x39 ,
0×00