websocket通讯原理

2,249 阅读3分钟

http通信只能一来一回,不能直接回包,于是出现了websocket实时通讯

背景

在websocket没有出现之前,总会有一些需求,需要获取数据,但是这些数据却不知道哪个时间点才能获取到,于是,大家使用了轮询来解决服务器推送的问题。

HTTP协议只能从客户端发出请求,服务器处理返回请求(这里可以看下图),但是服务器不能主动给客户端发送请求,于是解决服务器推送的问题,就只能靠轮询了。

轮询分为两种

  • 短轮询:定时发送http请求

  • 长轮询:发送请求直到收到消息or超时后继续发送下一个请求

但是,轮询并不能完美的解决服务器推送的问题

  • 服务器无法主动发送数据
  • 轮询实时性差
  • 轮询浪费较多资源

概念

websocket的出现,就是为了解决服务器推送的问题。

WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工(通信双方既是接收方也是发送方,两端设备可以同时发送和接收数据)通讯的协议。

特性

  • 建立在TCP链接之上
  • 较少的控制开销
  • 更强的实时性
  • 保持连接状态
  • 可以发送文本、二进制数据
  • 没有同源限制

链接过程

websocket的链接还是比较简单的,只要进行一次握手,就可以进行后续的通讯了,这里分为三步:

  1. http get请求将http请求升级为websocket请求
  2. 数据通讯
  3. 断开链接(可由任一方中断)

握手协议

在第一步的时候进行了握手,这里来看一下请求的详情

// 请求
GET / HTTP/1.1
Upgrade: websocket // 升级为websocket协议
Connection: Upgrade 
Host: localhost:3000 // 请求host
Origin: http://binnie.com // 请求来源
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== // 用于后续计算accept
Sec-WebSocket-Version: 13 // websocket版本号

// 回包
HTTP/1.1 101 Switching Protocols // 101表示链接成功
Upgrade: websocket // 与请求对应
Connection: Upgrade
// 使用key计算得来,尽量避免普通HTTP请求被误认为Websocket协议
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s= 

帧格式

WebSocket协议使用帧(Frame)收发数据

客户端发送给服务端的帧必须通过4字节的掩码(Masking-key)加密,服务端收到消息后,用掩码对数据帧的Payload Data进行异或运算解码得到数据,如果服务端收到未经掩码加密的数据帧,则应该马上关闭该WebSocket。

服务端发给客户端的数据则不需要掩码加密,客户端如果收到了服务端的掩码加密的数据,则也必须关闭它。

上图为websocket的帧格式,下面我们一个个来分析

这里从左往右看,横向为32位(bit)

  • FIN:最后的片段
  • RSV1-3:拓展定义
  • opcode:帧类型,其中控制帧:0x8 (Close), 0x9 (Ping), and 0xA (Pong),数据帧主要有:0x1 (Text), 0x2 (Binary)
  • MASK:数据是否掩码
  • Payload len:负载长度
  • Extended payload length:拓展负载长度
  • Masking-key:掩码
  • Playload Data:负载数据

所以,websocket传递数据时最小的请求头为 FIN+RSV1-3+opcode+MASK+Payload len+Masking-key = 6 byte(字节),剩下的就都是实际数据,相比于http头部小得多。

兼容性

websocket是很好的实时双向通讯,不过兼容性只到ie10,所以不支持websocket的浏览器就只能使用轮询等方法来兼容。

写在最后

由于低版本浏览器不支持websocket,需要使用轮询来兼容,于是出现了很多websocket库,后面再分析websocket库的实现。