4.2.2 发送服务端的握手响应当客户端对服务端建立了一个 WebSocket 连接之后,服务端必须完成接下来的步骤,以此去接受客户端的连接,并回应客户端的握手。
- 如果连接发生在 HTTPS(基于 TLS 的 HTTP)端口上,那么要执行一个 TLS 握手。如果 TLS 握手失败,就必须关闭连接;否则的话之后的所有通信都必须建立在加密隧道上。
- 服务端可以对客户端执行另外的授权认证,比如通过返回 401 状态码和 对应的 |WWW-Authenticate|,相关描述在
- 服务端也可以对客户端进行重定向,使用 3xx 状态码 。注意这一步也可以发生在上一步之前。
- 确认下面的信息:
[url=][/url]
/origin/客户端握手请求中的 |origin| 头字段表明了脚本在发起请求时所处的源。源被序列化成 ASCII 并且被转换成了小写。服务端可以选择性地使用这个信息去决定是否接受这个连接请求。如果服务端不验证源的话,那么它将接收来自任何地方的请求。
如果服务端不想接收这个连接的话,它必须返回适当的 HTTP 错误状态码(比如 403 Forbidden)并且终止接下来的 WebSocket 握手过程。更详细的内容,见第 10 节/key/客户端握手请求中的 |Sec-WebSocket-Key| 头字段包含了一个使用 base64 编码后的值,如果解码的话,这个值是 16 字节长的。这个编码后的值用于服务端生成表示其接收客户端请求的内容。服务端没有必要去将这个值进行解码。/version/客户端握手请求中的 |Sec-WebSocket-Version| 头字段包含了客户端希望进行通信的 WebSocket 协议的版本号。如果服务端不能理解这个版本号的话,那么它必须终止接下来的握手过程,并给客户端返回一个适当的 HTTP 错误状态码
(比如 426 Upgrade Required),同时在返回的信息中包含一个 |Sec-WebSocket-Version| 头字段,通过其值指明服务端能够理解的协议版本号。/subprotocol/服务端可以选择接受其中一个子协议,或者 null。子协议的选取必须来自客户端的握手信息中的 |Sec-WebSocket-Protocol| 头字段的元素集合。如果客户端没有发送 |Sec-WebSocket-Protocol| 头字段,或者客户端发送的 |Sec-WebSocket-Protocol|
头字段中没有一个可以被当前服务端接受的话,服务端唯一可以返回值就是 null。不发送这个头字段就表示其值是 null。注意,空字符串并不表示这里的 null 并且根据 RFC2616 中的 ABNF 定义,空字符串也是不合法的。根据协议中的描述,客户端握手请求中的
|Sec-WebSocket-Protocol| 是一个可选的头字段,所以如果服务端必须使用这个头字段的话,可以选择性的拒绝客户端的连接请求。/extensions/一个可以为空的列表,表示客户端希望使用的协议级别的扩展。如果服务端支持多个扩展,那么必须从客户端握手请求中的 |Sec-WebSocket-Extensions| 按需选择多个其支持的扩展。如果客户端没有发送次头字段,则表示这个字段的值是 null,
空字符并不表示 null。返回的 |Sec-WebSocket-Extensions| 值中不可以包含客户端不支持的扩展。这个字段值的选择和解释将在第 9 节中讨论[url=][/url]
- 如果服务端选择接受来自客户端的连接,它必须回答一个有效的 HTTP 响应:
[url=][/url]
一个状态行,包含了响应码 101。比如 HTTP/1.1 101 Switching Protocols一个 |Upgrade| 头字段,值为 websocket一个 |Connection| 头字段,值为 Upgrade一个 |Sec-WebSocket-Accept| 头字段。这个值通过连接定义在 4.2.2 节中的第 4 步的 /key/ 和字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11,连接后的字符串运用 SHA-1 得到一个 20 字节的值,
最后使用 base64 将这 20 个字节的内容编码,得到最后的用于返回的字符串。相应的 ABNF 定义如下:Sec-WebSocket-Accept = base64-value-non-emptybase64-value-non-empty = (1*base64-data [ base64-padding ]) | base64-paddingbase64-data = 4base64-characterbase64-padding = (2base64-character "==") | (3base64-character "=")base64-character = ALPHA | DIGIT | "+" | "/"注意:作为一个例子,如果来自客户端握手请求中的 |Sec-WebSocket-Key| 的值是 dGhlIHNhbXBsZSBub25jZQ== 的话,那么服务端需要将 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 字符串追加到其后,
变成 dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11,再对这个连接后的字符串运用 SHA-1 哈希得到这些内容
0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea,对于哈希后的内容进行 base64 编码,最后得到 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=,
然后将这个值作为服务端返回的头字段 |Sec-WebSocket-Accept| 的字段值。可选的,一个 |Sec-WebSocket-Protocol| 头字段,它的值已经在第 4.2.2 节中的第 4 步定义了可选的,一个 |Sec-WebSocket-Extensions| 头字段,它的值已经在第4.2.2 节中的第 4 步定义了。如果有服务端选择了多个扩展,可以将它们分别放在 |Sec-WebSocket-Extensions| 头字段中,或者合并到一起放到一个
|Sec-WebSocket-Extensions| 头字段中。[url=][/url]
这样就完成了服务端的握手。如果服务端没有发生终止的完成了所有的握手步骤,那么服务端就可以认为连接已经建立了,并且 WebSocket 连接的状态变为 OPEN。在这时,客户端和服务端就可以开始发送(或者接收)数据了。 |