WebSocket 协议是一种基于 TCP 协议的通信协议,它可以在客户端和服务器之间建立双向通信的连接,实现实时数据传输和交互操作。在Web应用程序中,WebSocket 协议可以替代 HTTP 协议的长轮询和短轮询技术,提供更高效和快速的通信方式。本篇文章旨在介绍 WebSocket 协议的基本概念、 API 应用和相关技术,帮助读者了解和掌握这一重要的通信协议。
WebSocket 的应用场景
- 即时通讯: WebSocket 使即时通讯成为可能,因为它可以在客户端和服务器之间实时传输数据。
- 在线游戏: 在网络游戏中,协议延迟是非常重要的,而 WebSocket 协议比 HTTP 协议更适合在线游戏,因为它能够实现实时通信。
- 社交媒体: 在社交媒体应用程序中,用户之间可以立即聊天和分享内容,WebSocket 协议使实时通信变得很容易。
- 股票市场: WebSocket 协议可以实现对股票市场的实时监测。它可以在需要时实时更新股市价格。
- 物联网: 物联网设备需要在客户端和服务器之间进行实时通信,以便进行实时监测和控制,而 WebSocket 协议可以实现这一点。
WebSocket 的优缺点
优点
- 双向通信: WebSocket允许服务器主动推送数据到客户端,而不必等待客户端请求,从而实现了双向通信。
- 实时性: 与HTTP请求–响应协议不同,WebSocket可以在客户端和服务器之间建立长久的连接,从而大大减少了通信延迟,实现实时性。
- 性能: 与轮询(Polling)和长轮询(Long-polling)相比,WebSocket连接是一次性的,只需要建立一次连接,之后通信过程中不用不断地发送请求进行数据交换,减少了服务器的负载,提高了性能。
- 节约流量: 由于WebSocket连接始终保持打开状态,因此不需要在每个HTTP请求中重复发送标头信息,从而减少数据包的大小。
缺点
- 兼容性问题: WebSocket技术还没有普及,一些老旧的浏览器不支持WebSocket,需要进行降级处理,同时也有些防火墙和代理可能会阻止WebSocket协议的使用。
- 保持连接: WebSocket连接始终保持打开状态,需要保持长久连接,长时间运行可能会导致资源消耗或权限被滥用。
- 安全性: WebSocket连接要求服务器端和客户端都具备保密性和数据完整性保护机制,因此在资源限制或网络不安全的环境下有可能会导致安全问题。
WebSocket 的连接原理
客户端发起 WebSocket 连接请求,请求头中包含 Upgrade 和 Connection 两个字段。Upgrade 字段指明协议升级,Connection 字段指明协议连接类型,如下所示:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务端接收到请求后,进行协议升级确认。如果服务端支持 WebSocket 协议,则返回状态码 101 Switching。Protocols 响应,表明接受协议升级请求,同时也会发送服务端的 Sec-WebSocket-Accept 头信息加密结果,如下所示:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
客户端收到服务端响应后,进行协议升级确认,验证服务端的 Sec-WebSocket-Accept 头信息加密结果是否正确。如果正确,表明连接已经升级成功,可以进行数据传输。
数据传输过程中,客户端和服务端可以双向发送或接收数据,数据格式为帧(Frame),帧是 WebSocket 传输的最小单位,包含了真实数据的二进制流以及控制信息,数据传输完毕后,可以关闭连接。
Websocket 数据帧格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
头部信息(Header)
- FIN(1位):表示这个数据帧是否是消息的最后一帧。如果是最后一帧,该值为1;否则为0。
- RSV1, RSV2, RSV3(各1位):预留字段,暂时没有使用,值一般为0。
Opcode(4位):指定操作代码。它可以有以下值:
- 0x0:表示数据帧是一个连续帧
- 0x1:表示数据帧是一个文本帧
- 0x2:表示数据帧是一个二进制帧
- 0x8:表示连接断开
- 0x9:表示ping
- 0xA:表示pong
- Mask(1位):如果设置为1,则需要一个掩码键(Masking Key),用于数据的安全传输。
- Payload length(7位或7+16位或7+64位):指定有效负载数据的长度(注意,这里指的是原始长度,没有应用掩码的长度)。如果它的值小于或等于125,则该字段就是有效负载的长度。如果它的值为126,则紧随其后的2个字节(16个比特位)用于指定长度。如果它的值为127,则紧随其后的8个字节(64个比特位)用于指定长度。
- 掩码键(Masking key) 掩码键是4个字节长的随机数,用于安全传输数据。
- 负载数据(Payload data) 负载数据是真正需要传输的消息数据。如果Mask标志为1,则需要对负载数据进行逐比特的异或操作(XOR)以加密数据。
用 WebSocket 写一个聊天室
我们可以使用 Nodejs + WebSocket 来写一个聊天室,这个例子非常简单,它实现了一个聊天室功能,用户可以通过表单输入消息并发送,消息会广播给所有连接的客户端。其中,服务器端使用 socket.io 库和 Express 框架搭建 WebSocket 服务器,监听客户端发送的 chat message 事件;客户端使用 socket.io 库连接 WebSocket 服务器,并监听服务器广播的 chat message 事件。
Nodejs
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
// 监听客户端连接
io.on('connection', (socket) => {
console.log('a user connected');
// 监听客户端发送的chat message事件
socket.on('chat message', (msg) => {
console.log('message: ' + msg);
// 将消息广播给所有客户端
io.emit('chat message', msg);
});
// 监听客户端断开连接
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
// 启动服务
server.listen(3000, () => {
console.log('listening on *:3000');
});
前端
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket Chat Room</title>
</head>
<body>
<ul id="messages"></ul>
<form id="message-form">
<input id="message-input" type="text">
<button type="submit">Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
// 连接到WebSocket服务器
const socket = io();
// 处理chat message事件
socket.on('chat message', (msg) => {
const item = document.createElement('li');
item.textContent = msg;
document.querySelector('#messages').appendChild(item);
});
// 处理表单提交事件
document.querySelector('#message-form').addEventListener('submit', (e) => {
e.preventDefault();
const input = document.querySelector('#message-input');
const message = input.value;
input.value = '';
socket.emit('chat message', message);
});
</script>
</body>
</html>
Apifox 调试 WebSocket 服务
如果你正在编写 WebSocket 服务并需要进行调试,我建议你使用一些很棒的 API 调试工具,如 Apifox,这款工具可以完美调试 WebSocket 服务~
谷歌插件
Apifox 有 Web端 和 客户端,如果你使用的是 Web端,想要调试本地服务,需要安装 Apifox 的谷歌插件。
下载地址:Apifox 谷歌浏览器插件
新建 WebSocket 请求
WebSocket 是客户端和服务端之间的长链接,因此你需要在 Apifox 中创建一个 WebSocket 请求,以便跳转到 Apifox 界面并填写相应的请求信息:
- 点击创建按钮
- 填入 WebSocket 服务的地址
- 可以选择填写 Message 和 Params
Message 和 Params
Message
在 Message 中,你可以填写要传递到服务端的信息。服务端将接收你发送的信息。
Params
在传输过程中,你也可以携带参数,可以通过在地址上添加查询参数完成,可以携带的数据类型包括:
- string
- integer
- number
- array
保存请求
写完所需信息之后,可以点击 保存按钮,进行保存,方便下次可继续调用。
连接、发送 WebSocket 请求
连接 WebSocket 服务
我们想要跟服务端进行 WebSocket 通信,就需要先建立一个 WebSocket 连接,我们只需要点击 连接 按钮。连接成功之后,Apifox 有成功的提示。
服务端那边也会因为连接成功,而做出响应。
发送 WebSocket 请求
接着我们就可以跟服务端进行 WebSocket 通信了。
我们可以使用 Apifox 点击发送按钮向服务端进行发送 Message、Params。
与 WebSocket 服务通信
发送之后,服务端也可以向客户端发送信息,例如我这里,我设置服务端每秒向客户端发送当时的时间戳~
这就是 Apifox 的 WebSocket 功能,非常方便。
与 WebSocket 断开连接
如果不想连接了,可以点击断开按钮。
关于 Apifox
欢迎体验一下,完全免费的哦:在线使用 Apifox。
Apifox 是全世界 API 工具中的佼佼者,它的好处包括:
- 简单易用: Apifox 提供直观易用的接口管理界面,使创建、管理和编辑接口变得简单
- 高效调试: Apifox 使用 Websocket 技术实现实时数据传输,支持多个操作系统和编程语言,使接口调试更加高效
- 完整的接口测试: Apifox 提供完整的接口测试工具,可帮助开发人员快速发现和修复接口问题
- 安全的API管理: Apifox 提供安全的API管理,使开发者可以安全地管理和保护他们的 API