简介

WebSocket 是一种和 HTTP 不同的协议。两者都位于 OSI 模型的应用层,并且都依赖于传输层的 TCP 协议,WebSocket 复用了 HTTP 的握手通道。

虽然和 HTTP 不同,但是 RFC 6455 中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries(WebSocket 通过 HTTP 端口 80 和 443 进行工作,并支持 HTTP 代理和中介),从而使 WebSocket 与 HTTP 协议兼容。

WebSocket 协议的最大特点就是服务器可以主动向客户端发送信息,客户端也可以向服务器发送信息,实现了双向通信。

特点

连接状态

WebSocket 创建连接之后就成为一种有状态的协议,之后的通信可以省略部分状态信息。而 HTTP 请求可能需要在每个请求都携带状态信息(如身份认证等)。

实时性

由于 WebSocket 协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少。

数据开销

WebSocket 协议有着较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有 2 至 10 字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的 4 字节的掩码。相对于HTTP 请求每次都要携带完整的头部,此项开销显著减少了。

二进制支持

WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容。

扩展

WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。

压缩

相对于 HTTP 压缩,WebSocket 在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

建立连接

WebSocket 请求通过 HTTP/1.1 协议的 101 状态码进行握手,同样包含请求报文和响应报文。不同浏览器的 WebSocket 最大连接数量不同:

  • IE:6
  • Chrome:256
  • Firefox:200
  • Safari:1273(Mac版本)

客户端:申请协议升级

1
2
3
4
5
6
7
8
9
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap, wamp
Sec-WebSocket-Version: 13
  • Connection:该字段必须设置为Upgrade,表示客户端要升级协议。
  • Upgrade:必须设置为websocket,表示客户端要求升级到WebSocket协议。
  • Sec-WebSocket-Key:浏览器随机生成基于 Base64 的密钥。服务器基于这个密钥生成响应首部的Sec-WebSocket-Accept
  • Origin:该字段是必须的,如果缺少Origin字段,服务器需要回复 HTTP 403 状态码(禁止访问)。
  • Sec-WebSocket-Version:表示支持的 WebSocket 版本(RFC6455 要求使用的版本是 13)。如果服务端不支持该版本,会返回一个 Sec-WebSocket-Version 头,里面包含服务端支持的版本号。
  • Sec-WebSocket-Protocol:表示不仅要传输任何数据,还要传输 SOAP 或 WAMP(“The WebSocket Application Messaging Protocol”)协议中的数据。
  • Sec-WebSocket-Extensions:由浏览器自动发送,其中包含其支持的所有扩展的列表。

服务器:响应协议升级

1
2
3
4
5
6
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: deflate-frame
Sec-WebSocket-Protocol: soap
  • Sec-WebSocket-Accept:服务器通过请求报文中的Sec-WebSocket-Key加密过后生成的值,浏览器通过这个值确保请求和响应对应,同样可以防止恶意连接或避免普通 HTTP 请求被误认为WebSocket协议。
  • Sec-WebSocket-Protocol:表示服务器仅支持所请求的子协议中的 SOAP。
  • Sec-WebSocket-Extensions:表示服务器支持客户端请求中的扩展。

Sec-WebSocket-Accept 的生成过程

  1. 将请求报文中的Sec-WebSocket-Key加上一个特殊的字符串。
  2. 计算 SHA-1 摘要,然后进行 Base64 编码,结果就为Sec-WebSocket-Accept头的值。
    1
    toBase64(sha1(Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const crypto = require('crypto');
    const magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
    const secWebSocketKey = 'w4v7O6xFTi36lq3RNcgctw==';

    let secWebSocketAccept = crypto.createHash('sha1')
    .update(secWebSocketKey + magic)
    .digest('base64');

    console.log(secWebSocketAccept);
    // Oy4NRAQ13jhfONC7bP8dTKb4PTU=

参考

  1. WebSocket-wikipedia
  2. WebSocket 教程-阮一峰的网络日志
  3. websocket-javascript.info
  4. WebSocket 是什么原理?为什么可以实现持久连接?-知乎
  5. WebSocket协议:5分钟从入门到精通