Websocket
Websocket
整理一下Websocket,方便以后问题查找
什么是Websocket ?
Websocket
是在TCP协议上封装的另一种应用层协议,主要用于Client 和Server 之间的全双工通信,WebSocket API也被W3C定为标准。因为本身设计的目的就是为了满足双工通信,所以解决了之前TCP 只能通过客户端请求服务端的限制,得到了比较广泛的使用。
HTTP
也是基于TCP 的一种常见的协议,是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。正是因为这些特性,Web开发中的大多数网络请求走的是Http 的协议,走的是短链接形式。
既然Websocket 是基于TCP的,那么可以先了解一下TCP 工作的方式。
TCP 的握手方式
直观的以gif 为例,建立连接需要三次握手
第一次握手:客户端发送请求报文将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 需要多一次,先经过三次握手,建立连接,然后再发送Upgrade
因为 Websocket 已经被作为了标准,主流的浏览器都已经支持,在使用方面也已经成熟。来看一下Http 和 Websocket 在发起连接的报文区别.
Websocket 请求头
1 | GET / HTTP/1.1 |
Http 请求头
1 | GET /hello.txt HTTP/1.1 |
1 | // 这两个是Websocket 与 Http 不同的部分 用来标识是使用Websocket 协议 |
Sec-WebSocket-Key
是由浏览器随机生成的,提供基本的防护,防止恶意或者无意的连接。
Sec-WebSocket-Version
表示 WebSocket 的版本,最初 WebSocket 协议太多,不同厂商都有自己的协议版本,不过现在已经定下来了。如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader
,里面包含服务端支持的版本号。
对于返回包
Websocket 返回包
1 | HTTP/1.1 101 Switching Protocols |
Http 返回包
1 | HTTP/1.1 200 OK |
对于Websocket 返回包
- 首先,101 状态码表示服务器已经理解了客户端的请求,并将通过
Upgrade
消息头通知客户端采用不同的协议来完成这个请求; - 然后,
Sec-WebSocket-Accept
这个则是经过服务器确认,并且加密过后的Sec-WebSocket-Key
; - 最后,
Sec-WebSocket-Protocol
则是表示最终使用的协议。
Sec-WebSocket-Accept
的计算方法:
- 将
Sec-WebSocket-Key
跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接; - 通过 SHA1 计算出摘要,并转成 base64 字符串。
注意: Sec-WebSocket-Key
/ Sec-WebSocket-Accept
的换算,只能带来基本的保障,但连接是否安全、数据是否安全、客户端 / 服务端是否合法的 ws 客户端、ws 服务端,其实并没有实际性的保证
Websocket 跟HTTP 进行比较
共同点:
- Websocket使用和 HTTP 相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。
- 两者都基于TCP ,提供可靠连接。
不同点:
- 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
- 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
- 保持连接状态。于HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
- 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
- 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
- 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
Websocket 通过 HTTP/1.1 协议的101状态码进行握手。 为了创建Websocket连接,需要通过客户端发出请求,之后服务器进行回应,这个过程通常称为“握手”。
常见问题如何分析
如果 返回码不是 101 那么出现的错误就是 Invalid HTTP status.
如果是 回包消息中没有 Upgrade
、 Connection
、 Sec-WebSocket-Key
、Sec-WebSocket-Accept
没有这几个货,错误消息是 A required HTTP header is missing
然后Websocket 内部就会自动走关闭connection 逻辑,执行 handle_terminate
, 根据之前的错误类型,就会执行回调给到 on_fail
或者是 on_close
同时会携带错误代码 , 比如 异常退出 1006, 正常退出 1000 等