一、什么是 WebSocket

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端

socket通信模型.png

二、为什么需要 WebSocket

初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

例如在目前 IDP 项目中有很多监控场景,比如在供电系统中,需要实现实时监控大量设备的运行情况。基本 HTTP 协议这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。目前能使用的方案,即是轮询。但是轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,选择了采用 WebSocket 来实现实时监控,根据服务器推送回来的信息进行数据更新。

三、特点及使用方式

WebSocket 最大的特点便是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。
9cb2a748556c4ce6decaddcf41e31ae9.jpg

还有其他以下特点:

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

协议标识符可以做这样的对比 http-ws,https-wss,整个请求地址是这样的

  1. ws://127.0.0.1:80/api/app/demo

基本的使用方法如下所示:

  1. if ("WebSocket" in window) {
  2. const ws = new WebSocket("ws://127.0.0.1:80/api/app/demo");
  3. ws.onopen = function() {
  4. console.log("连接成功");
  5. ws.send("你好啊,客户端");
  6. };
  7. ws.onmessage = function(evt) {
  8. var received_msg = JSON.parse(evt.data);
  9. if (received_msg) {
  10. console.log('这是我收到的消息:', received_msg)
  11. }
  12. ws.close();
  13. };
  14. ws.onclose = function() {
  15. console.log('断开了连接')
  16. };
  17. } else {
  18. // alert("浏览器不支持WebSocket");
  19. }

四、鉴权方式

实际项目中,服务器有必要针对特定的用户推送特定的消息。但是 WebSocket 是另一种协议,不兼容目前公司线上使用 cookie 进行鉴权的方式,啊啊啊,那该怎么办?

我们需要手动获取 token,贴心的程序员小哥哥们考虑到目前的鉴权无法满足 WebSocket,公共网关给我们加了一个查询 token 的接口,无需任何入参,即可满足您的需求

  1. /**
  2. * 获取 loginToken
  3. * 开发环境地址: common: 'http://yingzi-common-app.dev.yingzi.com'
  4. * */
  5. export function getLoginToken() {
  6. return request({
  7. url: `${baseURI.common}/api/common/app/v1/auth/current/token`,
  8. method: 'GET',
  9. });
  10. }

以IDP使用为例,在路径参数后加上token,如下所示:

  1. let wsInstance // 保存 ws 请求实例,用于页面销毁时主动调用 wsInstance.close() 断开连接
  2. if ("WebSocket" in window) {
  3. const ws = new ReconnectingWebSocket(`ws://${baseURI.idpWeb.split('//')[1]}/ws/central-dashboard/${id}/${token}`)
  4. wsInstance = ws
  5. ws.onopen = () => {
  6. console.log("连接成功")
  7. }
  8. ws.onmessage = (evt) => {
  9. try {
  10. const data = JSON.parse(evt.data)
  11. // 你的逻辑
  12. } catch (e) {}
  13. }
  14. ws.onclose = (e) => {
  15. console.log("断开了连接");
  16. }
  17. } else {
  18. console.log("浏览器不支持WebSocket");
  19. }

可以看到,我这里使用了 reconnecting-websocket 这个库,它是一个基于 websocket 优化的轻量级库,支持掉线重连等功能,用法和原生 websocket 完全一样,推荐使用