WebSocket 一篇文章带你搞懂

410 阅读5分钟

一、WebSocket是什么及使用的场景。

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。技术很成熟。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

使用的场景

1.社交聊天

微信,QQ,这一类社交聊天的app。

2.弹幕

视频软件及视频网站。他们的弹幕一直是一种特色。而且弹幕对于一个视频来说,很可能弹幕才是精华。发弹幕需要实时显示,也需要和聊天一样,需要即时。

3.协同编辑

现在很多开源项目都是分散在世界各地的开发者一起协同开发,此时就会用到版本控制系统,比如Git,SVN去合并冲突。但是如果有一份文档,支持多人实时在线协同编辑,那么此时就会用到比如WebSocket了,它可以保证各个编辑者都在编辑同一个文档,此时不需要用到Git,SVN这些版本控制,因为在协同编辑界面就会实时看到对方编辑了什么,谁在修改哪些段落和文字。

5.股票基金实时报价

金融界瞬息万变——几乎是每毫秒都在变化。如果采用的网络架构无法满足实时性,那么就会给客户带来巨大的损失。几毫秒钱股票开始大跌,几秒以后才刷新数据,一秒钟的时间内,很可能用户就已经损失巨大财产了。

6.体育实况更新

全世界的球迷,体育爱好者特别多,当然大家在关心自己喜欢的体育活动的时候,比赛实时的赛况是他们最最关心的事情。这类新闻中最好的体验就是利用Websocket达到实时的更新!

等等。。。

总结

从上面我列举的这些场景来看,一个共同点就是,高实时性!

二、如何使用

一个简单的示例

// 创建WebSocket连接
const socket = new WebSocket('ws://localhost:8080');

// 连接打开时触发
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// 接收到服务端消息时触发
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

// 连接被关闭时触发
ws.addEventListener('close', () => {
// do something});// 连接因错误而关闭时触发,例如无法发送数据时。
ws.addEventListener('error', () => {// do something});

 方法

// 关闭当前链接。

WebSocket.close([code[, reason]]);

// 传输给服务端数据

WebSocket.send(data);

一个完整的示例

export const socket = (url, callback) => {
  let lockReconnect = false;
  let timmer;

  // 心跳检测
  const heartCheck = {
    timeout: 20000,
    timeoutObj: null,
    serverTimeoutObj: null,
    start(ws) {
      const self = this;
      this.timeoutObj && clearTimeout(this.timeoutObj);
      this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
      this.timeoutObj = setTimeout(() => {
        // 这里发送一个心跳,后端收到后,返回一个心跳消息,
        // onmessage拿到返回的心跳就说明连接正常
        ws.send(JSON.stringify({
          action: 'PING',
          content: Date.parse(new Date()),
        }));
        self.serverTimeoutObj = setTimeout(() => {
          // 如果进行到这里说明 服务端接受了消息却没正常返回,我们关闭链接,并重新建立链接
          ws.close(); 
        }, self.timeout);
      }, this.timeout);
    },
  };

  // 重连  
  const reconnect = () => {
    if (lockReconnect) {
      return;
    }
    lockReconnect = true;
    // 没连接上会一直重连,设置延迟避免请求过多
    timmer && clearTimeout(timmer);
    timmer = setTimeout(() => {
      createWebSocket();
      lockReconnect = false;
    }, 4000);
  };

  // 监听WebSocket的状态  
const init = (ws) => {
    ws.addEventListener('close', () => {
      reconnect();
    });
    ws.addEventListener('error', () => {
      reconnect();
    });
    ws.addEventListener('open', () => {
      // 心跳检测重置
      heartCheck.start(ws);
    });
    ws.addEventListener('message', (event) => {
      // 拿到任何消息都说明当前连接是正常的
      callback(event.data);
      heartCheck.start(ws);
    });
  };

  // 创建WebSocket
  const createWebSocket = () => {
    try {
      const ws = new WebSocket(url);
      // 监听WebSocket的状态
      init(ws);
    } catch (e) {
      // 如果失败则重新链接
      reconnect();
    }
  };

  createWebSocket();
};
socket('`wss://xxx.xxx.xxx', (data) => {
    console.log(data, '数据拿到了,做你想做的事情吧');
});  

三、WebSocket心跳及重连机制

在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件。这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失。所以就需要一种机制来检测客户端和服务端是否处于正常的链接状态。因此就有了websocket的心跳了。还有心跳,说明还活着,没有心跳说明已经挂掉了。
同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连~