WebSocket на самом деле не так уж сложен

WebSocket

написать впереди

webSocket — это технология, которая позволяет серверу активно передавать данные клиенту. Функция журнала была написана несколько дней назад, и данные журнала необходимо обновлять в режиме реального времени. Бывает, что в проекте есть упакованный компонент WebSocket, а интерфейс поддерживает webSocket, поэтому он реализован. Это также первый раз, когда я использую его, я просто исследовал его и поделился им. Пример кода статьи находится здесь:GitHub.com/Nero Метод Nero использует…

Что такое веб-сокет

Прежде всего, вам нужно понять концепцию веб-сокета, следующее объяснение из Википедии

WebSocket — это протокол связи, обеспечивающий полнодуплексную связь по одному TCP-соединению. WebSocket упрощает обмен данными между клиентом и сервером, Позволяет серверу активно передавать данные клиенту. В WebSocket API браузеру и серверу нужно выполнить рукопожатие только один раз, и между ними может быть установлено постоянное соединение. И двусторонняя передача данных.

Прежде всего, необходимо понимать, что WebSocket — это протокол связи, отличный от протокола HTTP, который может реализовать только однократную передачу запроса клиента и ответа сервера. WebSocket может реализовать двустороннюю связь между клиентом и сервером.Проще говоря, самое большое и очевидное отличие заключается в том, что сервер может активно отправлять сообщения клиенту. Остальные особенности:

  • На этапе рукопожатия используется протокол HTTP.
  • Формат данных является легким и имеет низкую производительность. Когда клиент и сервер обмениваются данными, заголовок пакета данных от сервера к клиенту составляет всего от 2 до 10 байтов, а от клиента к серверу необходимо добавить еще одну 4-байтовую маску. HTTP должен каждый раз передавать полный заголовок.
  • Улучшенная поддержка двоичных файлов, возможность отправки текста и двоичных данных.
  • Нет ограничений по гомологии, любой клиент может общаться с сервером.
  • Идентификатор протокола — ws (или wss, если он зашифрован), а запрошенный адрес — это внутренний API, поддерживающий веб-сокеты.

Несколько способов связи с сервером в реальном времени

Все мы знаем, что обычно есть два способа взаимодействия с сервером в режиме реального времени без использования WebSocket. Опрос AJAX и длинный опрос.

AJAX-опрос

Опрос AJAX заключается в отправке запросов через регулярные промежутки времени, то есть нормальный процесс связи между клиентом и сервером, но он отправляется в бесконечном цикле Таким образом, можно гарантировать, что как только сервер получит последние новости, он может быть получен клиентом. .

Длинный опрос

Long Polling长轮询是客户端和浏览器保持一个长连接,等服务端有消息返回,断开。 然后再重新连接,也是个循环的过程,无穷尽也。 . .

Клиент инициирует длительный опрос, и если у сервера нет данных для возврата, Он будет удерживать запрос, и когда будут данные, он будет возвращен клиенту. Клиент снова инициирует длительный опрос и снова повторяет описанный выше процесс.

недостаток

Вышеупомянутые два метода имеют фатальную слабость, накладные расходы слишком высоки, и они пассивны. Предполагая высокий параллелизм, это тест для сервера. Возможности одноразового рукопожатия WebSocket, постоянного соединения и активной отправки могут решить вышеуказанные проблемы без ущерба для производительности.

Процесс подключения через WebSocket

Клиент инициирует рукопожатие HTTP, сообщает серверу о необходимости связи с протоколом WebSocket и сообщает версию протокола WebSocket. Сервер подтверждает версию протокола и обновляет ее до протокола WebSocket. После этого, если есть данные, которые необходимо передать, они будут активно переданы клиенту.

Когда подключение начинается, клиент использует протокол HTTP и протокол обновления сервера. После завершения обновления последующая обмен данными последовала протокол WebSocket. Давайте посмотрим на запрос заголовки

Accept-Encoding: gzip, deflate, br
Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
Cache-Control: no-cache
Connection: Upgrade
Host: 127.0.0.1:3000
Origin: http://localhost:3000
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: bwb9SFiJONXhQ/A4pLaXIg==
Sec-WebSocket-Version: 13
Upgrade: websocket

Ключевые поля таковы:

  • Соединение: Обновление означает обновление протокола
  • Обновление: веб-сокет для обновления протокола до протокола веб-сокета.
  • Sec-WebSocket-Version указывает версию веб-сокета. Если сервер не поддерживает версию, необходимо вернуть заголовок Sec-WebSocket-Version, который содержит номер версии, поддерживаемой сервером.
  • Sec-WebSocket-Key соответствует Sec-WebSocket-Accept в заголовке ответа сервера.Поскольку нет ограничения на одно и то же происхождение, клиент веб-сокета может произвольно подключаться к службам, поддерживающим веб-сокет. Это эквивалентно ключу и замку, избегая избыточных, бессмысленных соединений.

Взгляните на заголовки ответов ответа сервера.

Connection: Upgrade
Sec-WebSocket-Accept: 2jrbCWSCPlzPtxarlGTp4Y8XD20=
Upgrade: websocket

Ключ это поле

  • Sec-WebSocket-Accept: используется для информирования сервера о том, что он готов инициировать соединение через веб-сокет, значение рассчитывается в соответствии с ключом Sec-WebSocket-Key в заголовке клиентского запроса.

WebSocket API

Если клиент хочет связаться с сервером, который поддерживает webScoket, он может использовать конструктор WebSocket для возврата объекта WebSocket.

const ws = new WebSocket("ws://localhost:3000/websocket");

Таким образом, клиент начнет подключаться к серверу.

Свойства возвращенного экземпляра объекта:

  • WebSocket.onopen: обратный вызов после успешного подключения
  • WebSocket.onclose: обратный вызов после закрытия соединения
  • WebSocket.onerror: после сбоя обратного вызова
  • WebSocket.onmessage: клиент получает обратный вызов данных сервера
  • webSocket.bufferedAmount: количество двоичных байтов, не отправленных на сервер.
  • WebSocket.binaryType: используйте двоичный тип данных для подключения
  • WebSocket.protocol : подчиненный протокол, выбранный сервером.
  • WebSocket.url: абсолютный путь к WebSocket.
  • WebSocket.readyState: текущее состояние соединения, соответствующее четырем константам.
имя стоимость
WebSocket.CONNECTING 0
WebSocket.OPEN 1
WebSocket.CLOSING 2
WebSocket.CLOSED 3

метод:

  • WebSocket.close() закрывает текущее соединение
  • WebSocket.send(data) отправляет данные на сервер

Пример

После обсуждения стольких концепций я наконец-то понял, как их использовать. Реализация связи WebSocket требует сотрудничества клиента и сервера.

Написать пример, после того, как сервер запускает соединение, используйте таймер для активного отправки случайных номеров к клиенту, и клиент также может отправить на сообщение сервера. Затем сервер возвращает это сообщение клиенту. Клиент является JS + HTML, сервис реализован с Express + Express-WS. Код здесь:GitHub.com/Nero Метод Nero использует…. Вы можете клонировать его, установить зависимости и запустить npm start, чтобы увидеть эффект.

клиент

Передняя страница, окончательный эффект показан выше:

<body>
  <div class="websocket">
    <div class="receive">
      <p>服务端返回的消息</p>
      <div id="receive-box"></div>
    </div>
    <div class="send">
      <textarea type="text" id="msg-need-send"></textarea>
      <p>
        <button id="send-btn">点击发消息给服务端</button>
      </p>
    </div>
    <button id="exit">关闭连接</button>
  </div>
</body>

js код для использования webSocket находится здесь. Все, что он делает, это привязывает события к элементам страницы. Затем создайте объект WebSocket, прослушивайте события, такие как подключение объекта, получение сообщений и закрытие, и отправляйте данные обратно на страницу.

const msgBox = document.getElementById("msg-need-send")
const sendBtn = document.getElementById("send-btn")
const exit = document.getElementById("exit")
const receiveBox = document.getElementById("receive-box")

// 创建一个webSocket对象
const ws = new WebSocket("ws://127.0.0.1:3000/websocket/test")
ws.onopen = e => {
  // 连接后监听
  console.log(`WebSocket 连接状态: ${ws.readyState}`)
}

ws.onmessage = data => {
  // 当服务端返回数据的时候,放到页面里
  receiveBox.innerHTML += `<p>${data.data}</p>`
  receiveBox.scrollTo({
    top: receiveBox.scrollHeight,
    behavior: "smooth"
  })
}

ws.onclose = data => {
  // 监听连接关闭
  console.log("WebSocket连接已关闭")
  console.log(data);
}

sendBtn.onclick = () => {
  // 点击发送按钮。将数据发送给服务端
  ws.send(msgBox.value)
}
exit.onclick = () => {
  // 客户端主动关闭连接
  ws.close()
}

Сервер

С учетом модульной разработки прямо в файле нет кода, непосредственно создающего сервис. Вместо этого маршрутизация используется для назначения отдельного интерфейса службе webSocket.

const express = require("express");
const expressWs = require("express-ws")
const router = express.Router()
expressWs(router);

router.ws("/test", (ws, req) => {
  ws.send("连接成功")
  let interval
  // 连接成功后使用定时器定时向客户端发送数据,同时要注意定时器执行的时机,要在连接开启状态下才可以发送数据
  interval = setInterval(() => {
    if (ws.readyState === ws.OPEN) {
      ws.send(Math.random().toFixed(2))
    } else {
      clearInterval(interval)
    }
  }, 1000)
  // 监听客户端发来的数据,直接将信息原封不动返回回去
  ws.on("message", msg => {
    ws.send(msg)
  })
})


module.exports = router

Наконец, посмотрите на процесс взаимодействия данных

Суммировать

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

Статьи по Теме

Протокол WebSocket: 5 минут от входа до освоения

Учебное пособие по WebSocket - Жуан Ифэн

WebSocket API

документация по экспресс-WS

Добро пожаловать, чтобы обратить внимание на мою официальную учетную запись: один внешний интерфейс, один внешний интерфейс, я время от времени буду делиться знаниями о внешнем интерфейсе, которые я понимаю.