Websocket

Websocket

整理一下Websocket,方便以后问题查找

什么是Websocket ?

Websocket 是在TCP协议上封装的另一种应用层协议,主要用于Client 和Server 之间的全双工通信,WebSocket API也被W3C定为标准。因为本身设计的目的就是为了满足双工通信,所以解决了之前TCP 只能通过客户端请求服务端的限制,得到了比较广泛的使用。

HTTP 也是基于TCP 的一种常见的协议,是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。正是因为这些特性,Web开发中的大多数网络请求走的是Http 的协议,走的是短链接形式。

既然Websocket 是基于TCP的,那么可以先了解一下TCP 工作的方式。

TCP 的握手方式

直观的以gif 为例,建立连接需要三次握手

tcp_handshake

第一次握手:客户端发送请求报文将SYN = 1同步序列号和初始化序列号seq = x发送给服务端,发送完之后客户端处于SYN_Send状态。

第二次握手:服务端受到SYN请求报文之后,如果同意连接,会以自己的同步序列号SYN(服务端) = 1、初始化序列号seq = y和确认序列号(期望下次收到的数据包)ack = x+ 1以及确认号ACK = 1报文作为应答,服务器为SYN_Receive状态。

第三次握手: 客户端接收到服务端的SYN + ACK之后,知道可以下次可以发送了下一序列的数据包了,然后发送同步序列号ack = y + 1和数据包的序列号seq = x + 1以及确认号ACK = 1确认包作为应答,客户端转为established状态。

回归正题,继续讲解Websocket

websocket_connect

对于握手,Websocket 需要多一次,先经过三次握手,建立连接,然后再发送Upgrade

websocket_handshake

因为 Websocket 已经被作为了标准,主流的浏览器都已经支持,在使用方面也已经成熟。来看一下Http 和 Websocket 在发起连接的报文区别.

Websocket 请求头

1
2
3
4
5
6
7
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

Http 请求头

1
2
3
4
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
1
2
3
// 这两个是Websocket 与 Http 不同的部分 用来标识是使用Websocket 协议
Upgrade: websocket
Connection: Upgrade

Sec-WebSocket-Key 是由浏览器随机生成的,提供基本的防护,防止恶意或者无意的连接。

Sec-WebSocket-Version 表示 WebSocket 的版本,最初 WebSocket 协议太多,不同厂商都有自己的协议版本,不过现在已经定下来了。如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。

对于返回包

Websocket 返回包

1
2
3
4
5
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Http 返回包

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

对于Websocket 返回包

  1. 首先,101 状态码表示服务器已经理解了客户端的请求,并将通过 Upgrade 消息头通知客户端采用不同的协议来完成这个请求;
  2. 然后, Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
  3. 最后, Sec-WebSocket-Protocol 则是表示最终使用的协议。

Sec-WebSocket-Accept 的计算方法:

  1. Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
  2. 通过 SHA1 计算出摘要,并转成 base64 字符串。

注意: Sec-WebSocket-Key/ Sec-WebSocket-Accept 的换算,只能带来基本的保障,但连接是否安全、数据是否安全、客户端 / 服务端是否合法的 ws 客户端、ws 服务端,其实并没有实际性的保证

Websocket 跟HTTP 进行比较

共同点:

  1. Websocket使用和 HTTP 相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。
  2. 两者都基于TCP ,提供可靠连接。

不同点:

  1. 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
  2. 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
  3. 保持连接状态。于HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  4. 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  5. 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  6. 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

Websocket 通过 HTTP/1.1 协议的101状态码进行握手。 为了创建Websocket连接,需要通过客户端发出请求,之后服务器进行回应,这个过程通常称为“握手”。

常见问题如何分析

如果 返回码不是 101 那么出现的错误就是 Invalid HTTP status.

如果是 回包消息中没有 UpgradeConnectionSec-WebSocket-KeySec-WebSocket-Accept 没有这几个货,错误消息是 A required HTTP header is missing

然后Websocket 内部就会自动走关闭connection 逻辑,执行 handle_terminate , 根据之前的错误类型,就会执行回调给到 on_fail 或者是 on_close

同时会携带错误代码 , 比如 异常退出 1006, 正常退出 1000 等