Учебник по веб-сокетам

WebSocket

1. Что такое веб-сокет?

WebSocketЭто сетевой коммуникационный протокол, необходимый для многих дополнительных функций. Любой, кто новичок в WebSockets, задаст один и тот же вопрос: у нас уже есть протокол HTTP, зачем нам еще один протокол? Какую пользу это может принести? Ответ прост, потому что протокол HTTP имеет изъян: связь может инициировать только клиент. Например, если мы хотим узнать сегодняшнюю погоду, клиент может только отправить запрос на сервер, а сервер возвращает результат запроса. Протокол HTTP не может позволить серверу активно передавать информацию клиенту. Характеристики этого одностороннего запроса обречены быть очень неприятными для клиента, чтобы узнать, есть ли у сервера непрерывные изменения состояния. мы можем использовать только"опрос": время от времени отправляется запрос, чтобы узнать, есть ли на сервере новая информация. Самый типичный сценарий — чат. Опрос неэффективен и тратит ресурсы впустую (потому что вам нужно постоянно подключаться, иначе HTTP-соединение всегда открыто). Поэтому инженеры задумались, а есть ли способ лучше. Так был изобретен WebSocket.

Его самая большая особенность заключается в том, что сервер может активно передавать информацию клиенту, а клиент также может активно отправлять информацию серверу.Это настоящий двусторонний равноправный диалог, который принадлежиттехнология push сервератипа.

img

Другие функции включают в себя:

(1) Основываясь на протоколе TCP, реализация на стороне сервера относительно проста.

(2) Хорошая совместимость с протоколом HTTP. Порты по умолчанию также 80 и 443, а протокол HTTP используется на этапе рукопожатия, поэтому его нелегко экранировать во время рукопожатия, и он может проходить через различные прокси-серверы HTTP.

(3) Формат данных относительно легкий, производительность невелика, а связь эффективна.

(4) Можно отправлять текст, а также двоичные данные.

(5) Нет ограничений на одно и то же происхождение, и клиент может связываться с любым сервером.

(6) идентификатор протоколаws(если зашифровано,wss), URL-адрес сервера — это URL-адрес.

img

2. Простой пример WebSocket

var ws = new WebSocket("ws://echo.websocket.org");

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.");
 }

3. Введение в распространенные API WebSocket

  • конструктор веб-сокетов

    let ws = new WebSocket('ws://localhost:8080');
    

    После выполнения вышеуказанного оператора клиент подключится к серверу.

  • Состояние веб-сокета (readyState)

    Свойство readyState возвращает текущее состояние экземпляра объекта, всего существует четыре состояния.

    CONNECTING:值为0, 正在连接
    OPEN:值为1,连接成功
    CLOSING:值为2,连接正在关闭
    CLOSED:值为3,连接已经关闭
    
  • websocket.onopen (функция обратного вызова после успешного подключения)

    экземпляр объектаonopenАтрибут для указания функции обратного вызова после успешного подключения.

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

    Если вы хотите указать несколько функций обратного вызова, вы можете использоватьaddEventListenerметод.

    ws.addEventListener('open', function (event) {
      ws.send('Hello Server!');
    });
    
  • websocket.onclose (метод вызывается после закрытия)

    экземпляр объектаoncloseАтрибут для указания функции обратного вызова после закрытия соединения.

    ws.onclose = function(event) {
     console.log('onclose')
    }
    

    Если вы хотите указать несколько функций обратного вызова, вы можете использоватьaddEventListenerметод.

    ws.addEventListener("close", function(event) {
     console.log('onclose')
    });
    
  • websocket.onmessage (функция обратного вызова после получения данных сервера)

    ws.onmessage = function(event) {
      // 获取数据event.data
      var data = event.data;
      // 处理数据
    };
    
  • websocket.send (отправить данные на сервер)

    ws.send('your message')
    
  • websocket.onerror (метод вызывается при сообщении об ошибке)

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

4.Пребят сердцебиение

Websocket – это длинная связь между интерфейсом и сервером. Интерфейс и сервер также могут не установить соединение из-за некоторых ситуаций, и между ними нет напоминания об обратной связи. Поэтому, чтобы обеспечить устойчивость и стабильность соединения, появилось переподключение через веб-сокет. При использовании собственного веб-сокета, если сеть устройства отключена, любое событие веб-сокета не будет инициировано немедленно, и внешний интерфейс не сможет узнать, было ли разорвано текущее соединение. В это время, если вызывается метод websocket.send, браузер обнаружит, что ссылка отключена, и вызовет функцию onclose немедленно или через определенный период времени (разные браузеры или версии браузеров могут вести себя по-разному). Внутренняя служба веб-сокетов также может быть ненормальной, что приводит к отключению соединения. В это время внешний интерфейс не получает уведомления об отключении. Поэтому внешний интерфейс должен регулярно отправлять сообщение пульса. -end получает сообщение типа ping и немедленно возвращает сообщение pong, чтобы сообщить, что внешнее соединение нормальное. Если сообщение pong не получено в течение определенного периода времени, это означает, что соединение не является нормальным, и внешний интерфейс выполнит переподключение. Чтобы решить две вышеупомянутые проблемы, внешний интерфейс используется в качестве активной стороны, и сообщения ping регулярно отправляются для обнаружения проблем с сетью, внешним и внутренним подключением. После обнаружения исключения внешний интерфейс продолжает выполнять логику переподключения до тех пор, пока переподключение не будет успешным.

Общая функция обнаружения сердцебиения:

// 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间,如果已经断开了,发起重连。
let heartCheck = {
    // 心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
    timeout: 100000,
    serverTimeoutObj: null,
    reset: function() {
      clearTimeout(this.serverTimeoutObj)
      return this
    },
    start: function() {
      this.serverTimeoutObj = window.setInterval(() => {
        if (websocket.readyState === 1) {
          websocket.send('ping')
        } else {
          window.clearTimeout(this.serverTimeoutObj)
          // 处理逻辑:重连或者其他
        }
      }, this.timeout)
    }
  }

5. Пример полного кода WebSocket

function newWebSocket(option) {
  console.log('new webSocket.....')
  let websocket = null
  // 判断当前环境是否支持websocket
  if (window.WebSocket) {
    if (!websocket) {
      websocket = new WebSocket('你的请求地址')
    }
  } else {
    console.log('not support websocket')
  }
  // 连接成功建立的回调方法
  websocket.onopen = function(e) {
    // 成功建立连接后,重置心跳检测
    heartCheck.reset().start()
    console.log('connected successfully')
  }
  // 连接发生错误,连接错误时会继续尝试发起连接
  websocket.onerror = function() {
    console.log(`onerror`)
    newWebSocket()
  }
  // 接受到消息的回调方法
  websocket.onmessage = function(e) {
    console.log('onmessage', e.data)
    var message = e.data
    if (message) {
      // 执行接收到消息的操作
      if (option != undefined) {
        // 执行传入对象的方法,传出消息
        option.onmessage(message)
      }
    }
  }

  // 接受到服务端关闭连接时的回调方法
  websocket.onclose = function() {
    console.log('onclose')
  }
  // 监听窗口事件,当窗口关闭时,主动断开websocket连接,防止连接没断开就关闭窗口,server端报错
  window.onbeforeunload = () => {
    return websocket.close()
  }

  // 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间,如果已经断开了,发起重连。
  var heartCheck = {
    // 心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
    timeout: 100000,
    serverTimeoutObj: null,
    reset: function() {
      clearTimeout(this.serverTimeoutObj)
      return this
    },
    start: function() {
      this.serverTimeoutObj = window.setInterval(() => {
        if (websocket.readyState === 1) {
          websocket.send('ping')
        } else {
          console.log('websocket stop', websocket.readyState)
          window.clearTimeout(this.serverTimeoutObj)
          newWebSocket(option)
        }
      }, this.timeout)
    }
  }
  return websocket
}