WebSocket本身是基于TCP协议的,它将 TCP 的 Socket(套接字)应用在了web page上,从而使通信双方建立起一个保持在活动状态连接通道,并且属于全双工(双方同时进行双向通信)。
原理:
它是先借用 HTTP协议 的 101 switch protocol 来达到协议转换的,从HTTP协议切换成WebSocket通信协议。
特点:
1.服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。其他特点包括:
2.建立在 TCP 协议之上,服务器端的实现比较容易。
3.与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443 ,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
4.数据格式比较轻量,性能开销小,通信高效。
5. 可以发送文本,也可以发送二进制数据。
6.没有同源限制,客户端可以与任意服务器通信。
7.协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
握手:
客户端握手形式如下:
GET Test HTTP/1.1
Connection: Upgrade
Host: 118.222.33.44:8080
Sec-WebSocket-Key: Cuv8mYjZOwUCbL0MiUFAwA==
Sec-WebSocket-Version: 13
Upgrade: websocket
请求字段意义如下:
Connection: Upgrade:表示Upgrade是一个hop-by-hop的字段,这个字段是给proxy看的
如果没有该字段
Upgrade: websocket:表示要升级到 websocket 协议。这个字段是给最终处理请求的程序看的。
Sec-WebSocket-Version: 13:表示 websocket 的版本。如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader ,里面包含服务端支持的版本号。
Sec-WebSocket-Key:与后面服务端响应数据的 Sec-WebSocket-Accept是配套的,其值是16字节的随机数的被base64编码后的值。
服务器响应客户端握手请求形式如下:
HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
Sec-WebSocket-Accept:根据客户端请求的Sec-WebSocket-Key计算出来。
计算公式为:
将 Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。再进行SHA1加密和BASE64编码得到的值。
数据传输
数据帧格式
FIN:1 个比特。
如果是 1,表示这是 消息(message)
的最后一个分片(fragment)
,如果是 0,表示不是是 消息(message)
的最后一个 分片(fragment)
。
RSV1, RSV2, RSV3:各占 1 个比特。
一般情况下全为 0。当客户端、服务端协商采用 WebSocket 扩展时,这三个标志位可以非 0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用 WebSocket 扩展,连接出错。
Opcode: 4 个比特。
操作代码,Opcode 的值决定了应该如何解析后续的 数据载荷(data payload)
。如果操作代码是不认识的,那么接收端应该 断开连接(fail the connection)
。可选的操作代码如下:
- %x0:表示一个延续帧。当 Opcode 为 0 时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
- %x1:表示这是一个文本帧(frame)
- %x2:表示这是一个二进制帧(frame)
- %x3-7:保留的操作代码,用于后续定义的非控制帧。
- %x8:表示连接断开。
- %x8:表示这是一个 ping 操作。
- %xA:表示这是一个 pong 操作。
- %xB-F:保留的操作代码,用于后续定义的控制帧。
Mask: 1 个比特。
表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。
如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。
如果 Mask 是 1,那么在 Masking-key
中会定义一个 掩码键(masking key)
,并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask 都是 1。
Payload length:数据载荷的长度
单位是字节。为 7 位,或 7+16 位,或 1+64 位。
假设数 Payload length === x,如果
- x 为 0~126:数据的长度为 x 字节。
- x 为 126:后续 2 个字节代表一个 16 位的无符号整数,该无符号整数的值为数据的长度。
- x 为 127:后续 8 个字节代表一个 64 位的无符号整数(最高位为 0),该无符号整数的值为数据的长度。
此外,如果 payload length
占用了多个字节的话,payload length
的二进制表达采用 网络序(big endian,重要的位在前)
。
Masking-key:0 或 4 字节(32 位)
所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask 为 1,且携带了 4 字节的Masking-Key。如果 Mask 为 0,则没有Masking-Key。
备注:载荷数据的长度,不包括 mask key 的长度。
Payload data:(x+y) 字节
载荷数据:包括了扩展数据、应用数据。其中,扩展数据 x 字节,应用数据 y 字节。
扩展数据:如果没有协商使用扩展的话,扩展数据数据为 0 字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。
应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。
掩码对传输数据的处理:
Masking-Key表示掩码数据
Real-Data表示需要传输数据
MaskDeal-data表示掩码处理后的数据
i 表示处理数据的第几个字节
j = i % 4
MaskDeal-data[i] = Real-Data[i] ^ Masking-Key[j]
状态码:
状态码 | 名称 | 描述 |
---|---|---|
0–999 | 保留段, 未使用. | |
1000 | CLOSE_NORMAL | 正常关闭; 无论为何目的而创建, 该链接都已成功完成任务. |
1001 | CLOSE_GOING_AWAY | 终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开. |
1002 | CLOSE_PROTOCOL_ERROR | 由于协议错误而中断连接. |
1003 | CLOSE_UNSUPPORTED | 由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据). |
1004 | 保留. 其意义可能会在未来定义. | |
1005 | CLOSE_NO_STATUS | 保留. 表示没有收到预期的状态码. |
1006 | CLOSE_ABNORMAL | 保留. 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧). |
1007 | Unsupported Data | 由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据). |
1008 | Policy Violation | 由于收到不符合约定的数据而断开连接. 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景. |
1009 | CLOSE_TOO_LARGE | 由于收到过大的数据帧而断开连接. |
1010 | Missing Extension | 客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接. |
1011 | Internal Error | 客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接. |
1012 | Service Restart | 服务器由于重启而断开连接. |
1013 | Try Again Later | 服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接. |
1014 | 由 WebSocket 标准保留以便未来使用. | |
1015 | TLS Handshake | 保留. 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书). |
1016–1999 | 由 WebSocket 标准保留以便未来使用. | |
2000–2999 | 由 WebSocket 拓展保留使用. | |
3000–3999 | 可以由库或框架使用.不应由应用使用. 可以在 IANA 注册, 先到先得. | |
4000–4999 | 可以由应用使用. |