WebSocket 和 Server-Sent Events

141 阅读4分钟

WebSocket

为什么需要 WebSocket?

因为在此之前使用的是 Ajax 轮询,即特定的时间间隔(比如每秒)由浏览器发出请求,服务器返回最新数据

  • HTTP 请求包含的 头部信息较多,实际使用到的内容较少,导致宽带浪费
  • 服务器被动接受浏览器的请求然后响应,数据没有更新时仍要接收并处理请求,导致服务器CPU占用

WebSocket 可以解决:

  • WebSocket 压缩了头部信息,通常只有 2Bytes 左右
  • WebSocket 支持服务器主动推送消息,更好的支持实时通信

使用场景:

  • 实时双向的信息交互,如实时聊天,实时消息推送等。

WebSocket 是什么?

WebSocket 协议本质上是一个基于 TCP 的协议。可以实现浏览器和服务器全双工通信,即服务器主动发送信息给客服端。即 WebSocket 中,只需要浏览器和服务器完成一次握手,两者即可以创建持续性的连接。并进行双向数据传输。 关键字: 应用层协议、基于 TCP、全双工通信、一次握手、持久连接、双向数据传输

特点:

  • 建立在 TCP 协议之上
  • 与 HTTP 协议有良好的兼容性, 默认端口是 80(ws)和 443(wss,运行在 TLS 之上),并且握手阶段采用 HTTP 协议
  • 较小的开销:WebSocket 压缩了头部,HTTP 要携带完整的头部
  • 可以发送文本、二进制数据
  • 协议标识符是 ws(加密后为 wss),服务器网址就是 URL
  • 支持扩展:ws 协议定义了扩展,用户可以定义扩展协议,或实现自定义的自协议(比如支持自定义压缩算法)

客户端请求

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.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/

WebSocket 属性

WebSocket 属性

属性描述
Socket.readyState只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

WebSocket 事件

var Socket = new WebSocket(url, [protocol]);

以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

WebSocket 方法

方法描述
Socket.send()使用连接发送数据
Socket.close()关闭连接

WebSocket 使用

  • Connection: Upgrade 表示要升级协议;
  • Upgrade: websocket 表示要升级到 websocket 协议;
  • 设置 Sec-WebSocket-Key
// 创建 WebSocket 连接
const socket = new WebSocket("ws://localhost:8080");

// 连接以打开
socket.addEventListener("open", function (event) {
  socket.send("Hello Server!");
});

// 监听message消息
socket.addEventListener("message", function (event) {
  console.log("Message from server ", event.data);
});

Server-Sent Events

  • SSE 是介于 websocket、长短轮训之外的一种服务端推送的方式,用数据流的形式发送文本数据,可想象成网络视频的文字版。
  • 而客户端的代码在处理传入事件部分几乎与 websocket 相同。这是一个单向通道,所以你不能从客户端发送事件到服务器。

特点:

  • 使用简单,无需借用第三方库(socket.io)
  • 基于 HTTP 协议(WebSocket 是一个独立协议),无需对其做额外处理。还能享受 HTTP2 带来的优势
  • 默认支持断线重连
  • 支持自定义发送的消息类型

HTTP 头

Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
Connection: keep-alive
X-Accel-Buffering: no

SSE 使用

使用生成事件的脚本 URL 创建一个 EventSource 对象,用来开启与服务器的连接并从中接收事件。

const evtSource = new EventSource("ssedemo.php");

如果生成事件的脚本不同源,应该创建一个新的包含 URL 和 options 参数的 EventSource 对象。例如,假设客户端脚本在 example.com 上:

const evtSource = new EventSource("//api.example.com/ssedemo.php", {
  withCredentials: true,
});

自定义事件

evtSource.addEventListener("message", (event) => {
  let payload;

  try {
    // 从服务端接收的文本消息
    // 一般使用json进行消息编码
    // 普通纯文本也是可以处理,具体取决于前后端协议结果
    payload = JSON.parse(event.data);
    console.log("receiving data...", payload);
  } catch (error) {
    console.error("failed to parse payload from server", error);
  }
});

错误处理

evtSource.addEventListener("error", (err) => {
  console.error("error:", err);
});

关闭连接

evtSource.close();

两者区别

  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
  • SSE 默认支持断线重连,WebSocket 需要自己实现。
  • SSE 一般只用来传送文本,二进制数据需要自定义编码后传送,WebSocket 默认支持传送二进制数据。
  • SSE 支持自定义发送的消息类型。
  • SSE 是基于 HTTP 的,每次请求都需要重新建立连接,WebSocket 可以在客户端和服务端建立持久连接

参考链接