一、什么是 WebSocket
WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端
二、为什么需要 WebSocket
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
例如在目前 IDP 项目中有很多监控场景,比如在供电系统中,需要实现实时监控大量设备的运行情况。基本 HTTP 协议这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。目前能使用的方案,即是轮询。但是轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,选择了采用 WebSocket 来实现实时监控,根据服务器推送回来的信息进行数据更新。
三、特点及使用方式
WebSocket 最大的特点便是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。
还有其他以下特点:
- 建立在 TCP 协议之上,服务器端的实现比较容易
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此不容易屏蔽,能通过各种 HTTP 代理服务器
- 数据格式比较轻量,性能开销小,通信高效
- 可以发送文本,也可以发送二进制数据
- 没有同源限制,客户端可以与任意服务器通信
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
协议标识符可以做这样的对比 http-ws,https-wss,整个请求地址是这样的
ws://127.0.0.1:80/api/app/demo
基本的使用方法如下所示:
if ("WebSocket" in window) {
const ws = new WebSocket("ws://127.0.0.1:80/api/app/demo");
ws.onopen = function() {
console.log("连接成功");
ws.send("你好啊,客户端");
};
ws.onmessage = function(evt) {
var received_msg = JSON.parse(evt.data);
if (received_msg) {
console.log('这是我收到的消息:', received_msg)
}
ws.close();
};
ws.onclose = function() {
console.log('断开了连接')
};
} else {
// alert("浏览器不支持WebSocket");
}
四、鉴权方式
实际项目中,服务器有必要针对特定的用户推送特定的消息。但是 WebSocket 是另一种协议,不兼容目前公司线上使用 cookie 进行鉴权的方式,啊啊啊,那该怎么办?
我们需要手动获取 token,贴心的程序员小哥哥们考虑到目前的鉴权无法满足 WebSocket,公共网关给我们加了一个查询 token 的接口,无需任何入参,即可满足您的需求
/**
* 获取 loginToken
* 开发环境地址: common: 'http://yingzi-common-app.dev.yingzi.com'
* */
export function getLoginToken() {
return request({
url: `${baseURI.common}/api/common/app/v1/auth/current/token`,
method: 'GET',
});
}
以IDP使用为例,在路径参数后加上token,如下所示:
let wsInstance // 保存 ws 请求实例,用于页面销毁时主动调用 wsInstance.close() 断开连接
if ("WebSocket" in window) {
const ws = new ReconnectingWebSocket(`ws://${baseURI.idpWeb.split('//')[1]}/ws/central-dashboard/${id}/${token}`)
wsInstance = ws
ws.onopen = () => {
console.log("连接成功")
}
ws.onmessage = (evt) => {
try {
const data = JSON.parse(evt.data)
// 你的逻辑
} catch (e) {}
}
ws.onclose = (e) => {
console.log("断开了连接");
}
} else {
console.log("浏览器不支持WebSocket");
}
可以看到,我这里使用了 reconnecting-websocket 这个库,它是一个基于 websocket 优化的轻量级库,支持掉线重连等功能,用法和原生 websocket 完全一样,推荐使用