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 对象:
| 事件 | 事件处理程序 | 描述 |
|---|---|---|
| open | Socket.onopen | 连接建立时触发 |
| message | Socket.onmessage | 客户端接收服务端数据时触发 |
| error | Socket.onerror | 通信发生错误时触发 |
| close | Socket.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 可以在客户端和服务端建立持久连接
参考链接