tomoya06 / web-developer-guidance

Actually it's just a notebook for keeping down some working experience.
4 stars 0 forks source link

Network - WebSocket #31

Open tomoya06 opened 3 years ago

tomoya06 commented 3 years ago

WebSocket

概述

WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

WebSocket协议与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,利用请求头的Upgrade字段从HTTP协议更改为WebSocket协议。

WebSocket 定义的两个协议框架ws和wss与http类似,而且各自部分的要求也是在HTTP协议中使用的一样,各自的URI如下:

ws-URI = "ws://" host [ ":" port ] path [ "?" query ]
wss-URI = "wss://" host [ ":" port ] path [ "?" query ]

通信过程

image

本节参考MDN文档这篇博客

握手

当建立一个Websocket连接时,为了保持基于HTTP协议的服务器软件和中间件进行兼容工作,客户端打开一个连接时使用与HTTP连接的同一个端口到服务器进行连接,这样被设计为一个升级的HTTP请求。

客户端发送握手请求

客户端发送的请求报文如下:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

其中值得注意的请求头包括:

  1. Connection: Upgrade,以及
  2. Upgrade: websocket:这告诉服务器给升级到websocket协议
  3. Sec-WebSocket-Version:告诉服务器所使用的协议版本
  4. Sec-WebSocket-Key:是base64加密的字符串,浏览器自动生成。根据栈溢出网友的回答,这个字段以及后面服务器返回的Sec-WebSocket-Accept字段,本质上只是用来证明双方都是理解websocket这个协议的,没有其他加密的用途(WSS的SSL负责加密)。

服务端返回握手响应

当服务器收到握手请求时,它应该发回一个特殊的响应,表明协议将从HTTP变为WebSocket。响应报文如下:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

其中:

  1. Sec-WebSocket-Accept:参数很有趣(注:MDN说的),它需要服务器通过客户端发送的Sec-WebSocket-Key 计算出来。 怎样计算呢, 把客户发送的 Sec-WebSocket-Key 和 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (这个叫做 "魔法值")连接起来,把结果用SHA-1编码,再用base64编码一次,就可以了。

数据交换

客户端或服务端都可以在任何时间点发送数据——这就是WebSocket的魅力。

心跳

在经过握手之后的任意时刻里,无论客户端还是服务端都可以选择发送一个ping给另一方。 当ping消息收到的时候,接受的一方必须尽快回复一个pong消息。 例如,可以使用这种方式来确保客户端还是连接状态。

一个ping 或者 pong 都只是一个常规的帧, 只是这个帧是一个控制帧。如果在你有机会发送一个pong消息之前,你已经获取了超过一个的ping消息,那么你只发送一个pong消息。

关闭连接

客户端或服务器端都可以通过发送一个带有指定控制序列的控制帧以开始关闭连接握手。对端收到这个控制帧会回复一个关闭帧,关闭发起端关闭连接。任何在关闭连接后接收到的数据都会被丢弃。

特点

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。
  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

最佳实践

可以参考MDN文档或者阮一峰的博客尝试使用。