前端Websocket了解及其运用

4,247 阅读5分钟

一,认识websocket

HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。 WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。WebSocket是一个持久化的协议,这是相对于http非持久化来说的。

对大部分web前端开发者来说,其实只要记住几点:

  1. WebSocket可以在浏览器里使用
  2. 支持双向通信
  3. 使用很简单

1,有哪些优点

这里的对比参照物是HTTP协议,概括地说就是:支持双向通信,更灵活,更高效,可扩展性更好。

  1. 支持双向通信,实时性更强。
  2. 更好的二进制支持。
  3. 较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
  4. 支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
  5. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

2,需要学习些什么

最重要的往往就是连接建立过程数据交换教程。当然,数据的格式是逃不掉的,因为它直接决定了协议本身的能力。好的数据格式能让协议更高效、扩展性更好。

下面是参考图:

image.png

二,如何使用Websocket

对于web前端开发者来说,使用websocket其实还是很简单的,因为websocket本身就是广播-收听 模式(也有人称为发布-订阅模式),对此,前端开发者只需要进行建立连接-监听动作-操作动作这几个步骤,就能体验一系列websocket的流程

1,建立连接

var ws = new WebSocket("ws://你的域名或ip");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。 执行上面代码后,客户端就会与服务器进行连接。 我们会得到一个ws实例,其中实例中有许多属性,这里只挑平时经常使用的来讲

2, ws.readyState属性返回实例对象的当前状态,共有四种。

  CONNECTING:值为0,表示正在连接。
  OPEN:值为1,表示连接成功,可以通信了。
  CLOSING:值为2,表示连接正在关闭。
  CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

我们可以通过监听ws.readyState去判断socket的连接状态。

3, ws.onopen 实例对象的onopen属性,用于指定连接成功后的回调函数。

如连接成功后回调函数

```js
ws.onopen = function () {
  ws.send('Hello Server!');
}
```

4, ws.onclose 实例对象的.onclose属性,用于连接关闭后的回调函数。

ws.onclose = function(event) {
  // handle close event 用于连接关闭后,用户端需要执行的某些操作
};

5, ws.onmessage 实例对象的.onmessage属性,用于收到服务器返回数据后的回调函数。


ws.onmessage = function(event) {
  var data = event.data;
  // 处理数据
};

6, ws.send 方法用于向服务器发送数据。

发送的格式具体需要跟后端协商定义,比如发送普通文本:


ws.send('your message');


7, ws.onerror 用于连接报错时的回调函数。

一般连接报错,我们只需要执行重新连接就好了。


ws.onerror = function(event) {
  // handle error event
};

三,在对接websocket时遇到的问题

  1. 基于网络安全协议的条件,https不同源情况下连接socket需要使用wss://+域名 方式,后端必须使用端口443(并且不启动任何其他安全端口)还需要侦听反向代理并将所有wss流量传递到目标websocket服务器

  2. wss连接socket 服务器会避免资源浪费,在长时间(或nginx设定的超时时间)无操作情况下,socket会执行断开连接,需要我们做心跳检测操作,定时向后端发送心跳包

    例子:

    var heartCheck = {
      timer: 0,
      _obj : null,
      _callback:null,
      init: function(wsObj, callback) {
            console.log("init");
            this._obj = wsObj;
            callback && (this._callback = callback);
            this.sayHi();
      },
      sayHi: function() {
            clearTimeout(this.timer);
            this.timer = setTimeout(() => {
              this.onError();
            }, 10000);
            //连接成功,发送一个心跳包,并开启一个延时器,如果超过延时器设定的值未响应,执行onError方法
            this._obj.send("hi," + this.timer);
            console.log("sayHi:" + this.timer);
      },
      clear: function(flag) {
             //清除上一次的延时器,重新发送心跳包,如果flag传入为true 则断开
            console.log("clear:" + this.timer);
            clearTimeout(this.timer);
            if(true === flag){
              console.log("heartCheck finished");
              return;
            }
            setTimeout(()=>{
              this.sayHi();
            }, 3000);
      },
      onError: function() {
            console.log("onError:",this.timer);
            clearTimeout(this.timer);
            this._callback && this._callback();
      }
    };
    //let hc = new heartCheck();
    let uri = "ws://localhost:8080";
    var ws = new WebSocket(uri);
    ws.onopen = (event) => {
      console.log('ws onopen', event);
      ws.send('from client: hello');
      heartCheck.init(ws);
    };
    ws.onmessage = (event) => {
      console.log('ws onmessage');
      console.log('from server: ', event);
      showLog(event.data);
      heartCheck.clear();
    };
    ws.onclose = (event) => {
      console.log("ws close", event);
      console.log(ws);
      heartCheck.clear(true);
    };
    ws.onerror = (event) => {
      console.log("ws error", event);
      console.log(ws);
    };
    
  3. https不能请求http

    https地址中,如果加载了http资源,浏览器将认为这是不安全的资源,将会默认阻止,这就会给你带来资源不全的问题了,比如:图片显示不了,样式加载不了,JS加载不了。 因为样式类,基本上都是写在本地的,所以一般还可以,但是一些公共的js文件,往往就是存在于cdn或者其他服务器上,这时候,如果访问不了,可能就导致了业务就完全操作不了。比如:jquery效法加载失败,可能所有的操作、请求都将无效了。