SPDY
谷歌于 2012 年提出 SPDY(取自 SPeeDY,发音同 speedy) ,其开发目标旨在解决 HTTP 的性能瓶颈,缩短 Web 页面的加载时间 ,属于增强 HTTP 协议。SPDY 一直处于草案阶段,现在已经被 HTTP/2.0 取代,Chrome 51 已经移除对 SPDY 的支持。
结构设计
SPDY 官方定义为会话层协议(OSI 模型),在 TCP/IP 模型中可以归类为应用层,介于 TLS/SSL 与 HTTP 之间。类似于前面讲过的 HTTPS 设计(传送门),不会改变现有的 HTTP 的实现,对于实际应用几乎透明。
OSI 将计算机网络体系结构划分为七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
其中会话层的作用:允许用户使用简单易记的名称建立连接 相当于公司中收寄信、写信封与拆信封的秘书。
SPDY 为 HTTP 提供的改进
多路复用
通过单一的 TCP 连接,可以无限制处理多个 HTTP 请求。所有请求的处理都在一条 TCP 连接上完成,因此 TCP 的处理效率得到提高。
赋予请求优先级
SPDY 不仅可以无限制地并发处理请求,还可以给请求逐个分配优先级顺序。这样主要是为了在发送多个请求时,解决因带宽低而导致响应变慢的问题。
压缩 HTTP 报文头
解决了 HTTP 只能压缩报文体,不能压缩报文头的问题。通信产生的数据包数量和发送的字节数就更少了。
推送功能
支持服务器主动向客户端推送数据的功能。服务器可直接发送数据,而不必等待客户端的请求,打破了 HTTP 协议的客户/服务器模式。
服务器提示功能
服务器可以主动提示客户端请求所需的资源。由于在客户端发现资源之前就可以获知资源的存在,因此在资源已缓存等情况下,可以避免发送不必要的请求。
强制使用 HTTPS
为了更加安全,SPDY 强制要求建立 STL/SSL 连接。
WebSocket 协议
WebSocket,即 Web 浏览器与 Web 服务器之间全双工通信标准。其中,WebSocket 协议由 IETF 定为标准,WebSocket API 由 W3C 定为标准。作为基于 HTTP 的补充协议,可以实现双工通信,完全打破 HTTP 客户/服务器模式,如果把 Ajax 比做发电报,那 WebSocket 就好比打电话。
结构设计
WebSocket 是基于 HTTP 的渐进式升级,它依赖 HTTP 完成“握手”,如果一方不支持该协议,则可以由开发者选择降级方案。由于是建立在 HTTP 基础上的协议,因此连接的发起方仍是客户端,而一旦确立 WebSocket 通信连接,不论服务器还是客户端,任意一方都可直接向对方发送报文。
WebSocket的“握手”
客户端请求
请求报文头里新增:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <随机加密串>
Sec-WebSocket-Protocol: <自定义服务名>
Sec-WebSocket-Version: <协议版本号>
服务器应答
状态码:101 Switching Protocols
响应报文头里新增:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <加密key>
Sec-WebSocket-Protocol: <自定义服务名>
WebSocket 为 HTTP 提供的改进
推送功能
这是全双工通信带来的结果之一,支持由服务器向客户端推送数据的推送功能,也就是说服务器可以在客户端未请求的情况下,主动发送数据给客户端。这种需求在即时通讯等场景很常见。
减少通信量
理论上 WebSocket 就是为了长连接设计的,一旦建立连接就不会断开,也就是说不需要频繁“握手”。和 HTTP 相比,不但每次连接时的总开销减少,而且由于 WebSocket 的首部信息很小,通信量也相应减少了。
降级方案
Ajax 轮询和Ajax 长轮询(Long Poll),详见进阶篇的连接管理模型(传送门)。
实践示例
nodejs 可以使用 nodejs-websocket 包实现 WebSocket 服务。现代的浏览器均可以支持 WebSocket 的 API。
下面代码可以在本地演示了客户端报单数,服务器报双数的循环交互。
// 服务器,会监听8001端口。
const ws = require('nodejs-websocket')
const port = 8001
const server = ws.createServer(function (conn) {
conn.on('text', function (str) {
// 接收客户端的消息
console.log('client:' + str)
if (str === 'what`s your name?') {
// 应答问候语
conn.sendText('My name is webSocket server!')
} else {
// 累加数字
conn.sendText(JSON.stringify(Number(str) + 1))
}
})
conn.on('close', function (code, reason) {
console.log('webSocket close')
})
conn.on('error', function (code, reason) {
console.log('webSocket error')
})
})
.listen(port)
console.log(`webSocket listen on ${port} successfully`)
// 客户端,将这代码引入一个html文件,用浏览器打开html即可。
if (!window.WebSocket) {
console.error('browser does not support WebSocket')
return
}
const ws = new WebSocket('ws://localhost:8001')
let count = 0
ws.onopen = function (e) {
console.log('connection success')
// 问候语
ws.send('what`s your name?')
ws.send(count)
}
ws.onclose = function (e) {
console.warn('connection close')
}
ws.onerror = function () {
console.error('connection error')
}
// 接收服务器的消息
ws.onmessage = function (e) {
let message = 'server:' + e.data + ''
console.log(message)
if (e.data !== 'My name is webSocket server!') {
// 累加数字
setTimeout(() => {
ws.send(Number(e.data) + 1)
}, 1000)
}
}
WebDAV
WebDAV(Web-based Distributed Authoring and Versioning),即基于万维网的分布式创作和版本控制。是一个可对 Web 服务器上的内容直接进行文件复制、编辑等操作的分布式文件系统。它作为扩展 HTTP/1.1 的协议定义在 RFC4918。但因为对实时性和安全性的问题,实际上很少用,虽然类似于当今的流行的网盘,但他们本质是不同的。
结构设计
WebDAV 的实现是对 HTTP/1.1 的扩展,在原有的请求方法和状态码的基础上,针对服务器上的资源,新增加了一些概念。
- 集合(Collection):是一种统一管理多个资源的概念。以集合为单位可进行各种操作。也可实现类似集合的集合这样的叠加。
- 资源(Resource):把文件或集合称为资源。
- 属性(Property):定义资源的属性。定义以“名称 = 值”的格式执行。
- 锁(Lock):把文件设置成无法编辑状态。多人同时编辑时,可防止在同一时间进行内容写入。
追加的请求方法
方法 | 用途 |
---|---|
PROPFIND | 获取属性 |
PROPPATCH | 修改属性 |
MKCOL | 创建集合 |
COPY | 复制资源及属性 |
MOVE | 移动资源 |
LOCK | 资源加锁 |
UNLOCK | 资源解锁 |
新增状态码
状态码 | 含义 |
---|---|
102 Processing | 可正常处理请求,但目前是处理中状态 |
207 Multi-Status | 存在多种状态 |
422 UnprocessibleEntity | 格式正常,内容有误 |
423 Locked | 资源已被加锁 |
424 FailedDependency | 处理与某请求关联的请求失败,因此不再维持依赖关系 |
507 InsufficientStorage | 保存空间不足 |
HTTP/2.0
概述
HTTP/2.0 是 HTTP 协议自 1999 年 HTTP/1.1 发布后的首个更新,主要基于 SPDY 协议。
HTTP/2.0 基本上都能解决上面提到的有关 HTTP/1.0 遇到的瓶颈。
分层结构
HTTP/2.0 在原有的 HTTP 层前加入了分帧层,类似与 SPDY 协议的结构,分帧层介于 STL/SSL 与 HTTP 之间。
- 分帧层:HTTP/2.0 特性的核心部分;
- 数据/HTTP层:其中包含传统上被认为是 HTTP 及其关联数据的部分。
特点:
- 二进制分帧
HTTP/2.0 的分帧层是基于帧的二进制协议,最小消息单位是帧。这方便了机器解析,解析更加高效。每个数据流都以消息的形式发送,而消息由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。 - 首部压缩
H2 在客户端和服务端使用“首部表”来跟踪和存储之前发送的头部键值对,每次只需要差量更新,不需要重复交换;
首部表在 H2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
头部内容也会被压缩处理。 - 多路复用
基于二进制分帧的特性,所有请求通过一个 TCP 连接并发完成,不在依赖多开 TCP 连接去实现并行加载。 - 并行双向字节流的请求和响应
基于二进制分帧的特性,同一个 TCP 连接上同时有多个不同方向的数据流,因此客户端可以一边乱序发送,一边接收响应,服务器也如此。 - 加密传输
虽然协议并未严格限制,但现代浏览器都要求必须使用 HTTPS,已成为了事实上的强制标准。 - 服务器推送
服务器可以主动将 HTML 页面相关的 js 和 css 文件推送给浏览器,不需要等待浏览器解析 HTML 后再发起请求获取。遵循同源策略,浏览器可拒绝。 - 请求优先级
基于二进制分帧的特性,请求可以带上 32bit 的优先级值,0 表示最高优先级,数值越大优先级越低,可以制定策略优化传输。
存在的问题
HTTP/2.0 虽然已经解决了 HTTP/1.0 的诸多瓶颈问题,但依然存在问题,这些问题基本都是由于TCP协议本身的限制导致的。
- 队头阻塞:这是 TCP 协议本身可靠性的重传机制导致的问题,如果出现丢包,那就会不断重试。这时候又只有一条 TCP 连接,所以会导致性能还不如 HTTP1.1。
- 握手延迟大:TCP 协议的三次握手依然无法避免。
QUIC
概述
QUIC 是 Quick UDP Internet Connections的缩写,读作 quick。由 Google 开发。
可以认为 QUIC 是为了解决 HTTP/2.0 在 TCP 遇到的瓶颈,而在 UDP 上做探索所设计的方案。(参考上面 HTTP2 的存在问题)
特性
- 没有队头阻塞的多路复用。
- UDP 协议依据 id 寻找目标设备,在多变的移动网络有优势。
- 前向纠错,每个包还包括其他包的部分信息,可以在丢包的情况下依靠已接受的包的冗余数据拼凑出丢失的部分,减少重传。
- 不需要多次握手,降低延迟。