Быстро внедрить приложение для связи в реальном времени на основе socket.io

Node.js WebSocket

С развитием веб-технологий сценарии использования и требования становятся все более сложными, и клиента уже не устраивает простой запрос на получение статуса. Связь в режиме реального времени все больше используется в различных сферах.

HTTP является наиболее часто используемой технологией связи между клиентом и сервером, но HTTP-связь может быть инициирована только клиентом и не может своевременно получать изменения данных на сервере. Вы можете полагаться только на периодический опрос, чтобы получить последний статус. Своевременность не может быть гарантирована, и большее количество запросов увеличит нагрузку на сервер.

Появилась технология WebSocket.

Концепция веб-сокета

В отличие от HTTP PROMI-DUPLEUPLEX WebSocket - это полнодуплексный протокол на основе подключения TCP, поддерживает двустороннюю связь сервера клиента.

WebSocketОбмен данными между клиентом и сервером упрощается, что позволяет серверу активно передавать данные клиенту. В API WebSocket браузеру и серверам нужно только выполнить рукопожатие, и они могут создавать постоянные соединения и выполнять двустороннюю передачу данных.

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

HTTP与websocket对比

выполнить

собственная реализация

Объект WebSocket поддерживает в общей сложности четыре сообщения onopen, onmessage, onclose и onerror.

установить соединение

Соединение WebSocket можно быстро установить с помощью javascript:

    var Socket = new WebSocket(url, [protocol] );

Первый параметр в приведенном выше кодеurl, указывает URL-адрес подключения. второй параметрprotocolявляется необязательным и указывает допустимые подпротоколы.

Использовать с http-протоколомhttp://Как и в начале, URL-адрес протокола WebSocket используетws://В начале используется дополнительно безопасный протокол WebSocketwss://начало.

  1. Когда соединение между браузером и WebSocketServer установлено успешно, будет запущено сообщение onopen.
    Socket.onopen = function(evt) {};
  1. В случае сбоя соединения, сбоя отправки или получения данных или возникновения ошибки при обработке данных браузер выдает сообщение об ошибке.
    Socket.onerror = function(evt) { };
  1. Когда браузер получает запрос на закрытие соединения, отправленный WebSocketServer, запускается сообщение о закрытии.
    Socket.onclose = function(evt) { };

отправлять и получать сообщения

  1. Когда браузер получает данные, отправленные WebSocketServer, он инициирует сообщение onmessage, а параметр evt содержит данные, переданные сервером.
    Socket.onmessage = function(evt) { };
  1. send используется для отправки сообщения на сервер.
    Socket.send();

socket

WebSocket предлагался вместе с HTML5, поэтому есть проблема с совместимостью.В это время появилась очень полезная библиотека——Socket.io.

socket.io инкапсулирует websocket и включает в себя другие методы подключения.Вы можете использовать socket.io для установки асинхронных подключений в любом браузере. socket.io содержит библиотеку сервера и клиента.Если в браузере используется js socket.io, сервер также должен быть применим.

socket.io — это библиотека связи клиент-сервер в реальном времени, основанная на Websocket.

Нижний уровень socket.io основан на библиотеке engine.io. engine.io — это низкоуровневая библиотека для socket.io, обеспечивающая двунаправленную связь между браузерами и устройствами. engine.io использует Websocket и XHR для инкапсуляции набора протоколов сокетов. В более ранних версиях браузеров Websocket не поддерживается, и вместо него для совместимости используется длинный опрос.

engine.io

Документация API

Socket.io позволяет вам запускать пользовательские события или реагировать на них.Вы можете запускать любое пользовательское имя события, кроме подключения, сообщения и отключения, которые нельзя использовать.

установить соединение

    const socket = io("ws://0.0.0.0:port"); // port为自己定义的端口号
    let io = require("socket.io")(http);
    io.on("connection", function(socket) {})

Обмен сообщениями

1. Отправка данных

    socket.emit(自定义发送的字段, data);

2. Получить данные

    socket.on(自定义发送的字段, function(data) {
        console.log(data);
    })

Отключить

1. Отключить все

    let io = require("socket.io")(http);
    io.close();

2. Клиент отключается от сервера

    // 客户端
    socket.emit("close", {});
    // 服务端
    socket.on("close", data => {
        socket.disconnect(true);
    });

Комната и пространство имен

Иногда вебсокет имеет следующие сценарии использования: 1. Сообщения, отправляемые сервером, классифицируются, и разные клиенты должны получать разные классификации 2. Серверу не нужно отправлять сообщения всем клиентам, только для определенного Отправлять сообщения определенным группы;

Для этого сценария использования используются очень практичное пространство имен и место в сокете.

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

namespace与room的关系

namespace

Сервер

    io.of("/post").on("connection", function(socket) {
        socket.emit("new message", { mess: `这是post的命名空间` });
    });
    
    io.of("/get").on("connection", function(socket) {
        socket.emit("new message", { mess: `这是get的命名空间` });
    });

клиент

    // index.js
    const socket = io("ws://0.0.0.0:****/post");
    socket.on("new message", function(data) {
        console.log('index',data);
    }
    
    //message.js
    const socket = io("ws://0.0.0.0:****/get");
    socket.on("new message", function(data) {
        console.log('message',data);
    }

room

клиент

    //可用于客户端进入房间;
    socket.join('room one');
    //用于离开房间;
    socket.leave('room one');

Сервер

    io.sockets.on('connection',function(socket){
        //提交者会被排除在外(即不会收到消息)
        socket.broadcast.to('room one').emit('new messages', data);
        // 向所有用户发送消息
        io.sockets.to(data).emit("recive message", "hello,房间中的用户");      
    }

Пример получения информации в реальном времени с помощью socket.io

Наконец дошли до стадии применения, сервер используетnode.jsМокайте интерфейс сервера. Следующие примеры реализованы на локальном сервере.

Сервер

Давайте сначала посмотрим на сервер, сначала запустим службу, установим ееexpressа такжеsocket.io

Установить зависимости

    npm install --Dev express
    npm install --Dev socket.io

построить узел сервера

    let app = require("express")();
    let http = require("http").createServer(handler);
    let io = require("socket.io")(http);
    let fs = require("fs");
    
    http.listen(port); //port:输入需要的端口号
    
    function handler(req, res) {
      fs.readFile(__dirname + "/index.html", function(err, data) {
        if (err) {
          res.writeHead(500);
          return res.end("Error loading index.html");
        }
    
        res.writeHead(200);
        res.end(data);
      });
    }
    
    io.on("connection", function(socket) {
        console.log('连接成功');
        //连接成功之后发送消息
        socket.emit("new message", { mess: `初始消息` });
        
    });

клиент

Основной код - index.html (отправка данных на сервер)

    <div>发送信息</div>
    <input placeholder="请输入要发送的信息" />
    <button onclick="postMessage()">发送</button>
    // 接收到服务端传来的name匹配的消息
    socket.on("new message", function(data) {
      console.log(data);
    });
    
    function postMessage() {
      socket.emit("recive message", {
        message: content,
        time: new Date()
      });
      messList.push({
        message: content,
        time: new Date()
      });
    }

Код ядра — message.html (получение данных с сервера)

    socket.on("new message", function(data) {
      console.log(data);
    });

Эффект

эффект общения в реальном времени

实时通讯效果

Все клиенты отключены

全部断开

Клиент отключается

某客户端断开连接

приложение пространства имен

namespace

присоединиться к комнате

加入房间

покинуть комнату

离开房间

приложение в рамках

npm install socket.io-client

    const socket = require('socket.io-client')('http://localhost:port');

    componentDidMount() {
        socket.on('login', (data) => {
            console.log(data)
        });
        socket.on('add user', (data) => {
            console.log(data)
        });
        socket.on('new message', (data) => {
            console.log(data)
        });
    }

Анализ протокола webSocket

Headers

Headers

запросить пакет

    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    Cache-Control: no-cache
    Connection: Upgrade
    Cookie: MEIQIA_VISIT_ID=1IcBRlE1mZhdVi1dEFNtGNAfjyG; token=0b81ffd758ea4a33e7724d9c67efbb26; io=ouI5Vqe7_WnIHlKnAAAG
    Host: 0.0.0.0:2699
    Origin: http://127.0.0.1:5500
    Pragma: no-cache
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    Sec-WebSocket-Key: PJS0iPLxrL0ueNPoAFUSiA==
    Sec-WebSocket-Version: 13
    Upgrade: websocket
    User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1

Описание пакета запроса:

  • Должен быть допустимый формат http-запроса;
  • Метод HTTP-запроса должен быть GET, а протокол должен быть не ниже 1.1, например: Get / HTTP/1.1;
  • Поле заголовка Upgrade должно быть включено, и его значение равно «websocket», чтобы сообщить серверу, что это соединение необходимо обновить до websocket;
  • НЕОБХОДИМО включать поле заголовка «Соединение» со значением «Обновление»;
  • ДОЛЖЕН включать поле заголовка «Sec-WebSocket-Key», значение которого представляет собой случайную последовательность символов длиной 16 байт, закодированную в base64;
  • Если запрос исходит от клиента браузера, необходимо также включить поле заголовка Origin. Это поле заголовка используется для предотвращения несанкционированных атак с использованием междоменных сценариев, и сервер может решить, принимать ли соединение WebSocket от Origin;
  • Должен включать поле заголовка «Sec-webSocket-Version», которое является номером версии используемого в настоящее время протокола, и текущее значение должно быть 13;
  • Может включать «Sec-WebSocket-Protocol», указывающий список протоколов, поддерживаемых клиентом (приложением), и сервер выбирает один или ни одного приемлемого ответа протокола;
  • Может включать «Sec-WebSocket-Extensions», расширения протокола, определенный тип протокола может поддерживать несколько расширений, с помощью которых могут быть достигнуты улучшения протокола;
  • Может включать произвольные другие домены, такие как файлы cookie.

пакет ответа

Описание пакета ответа:

    Connection: Upgrade
    Sec-WebSocket-Accept: I4jyFwm0r1J8lrnD3yN+EvxTABQ=
    Sec-WebSocket-Extensions: permessage-deflate
    Upgrade: websocket
  • Поле заголовка Upgrade должно быть включено со значением «websocket»;
  • ДОЛЖЕН включать поле заголовка Connection со значением «Upgrade»;
  • Должно быть включено поле заголовка Sec-WebSocket-Accept, значение которого является значением пакета запроса «Sec-WebSocket-Key», а строка «258EAFA5-E914-47DA-95CA-C5AB0DC85B11» конкатенирована, а затем конкатенированные символы Строка подвергается операции sha-1, а затем кодированию base64, что является значением "Sec-WebSocket-Accept";
  • В ответном пакете после двоеточия есть пробел;
  • Наконец, в конце пакета ответа требуются две пустые строки.

запросить данные

    EIO: 3
    transport: websocket
    sid: 8Uehk2UumXoHVJRzAAAA
  • EIO:3 указывает, что используется протокол engine.io версии 3.
  • транспорт указывает тип используемого транспорта
  • sid: идентификатор сеанса (строка)

Frames

Протокол WebSocket использует фреймы для отправки и получения данных, и вы можете просмотреть данные отправленных фреймов в консоли -> Фреймы.

Что означают цифры перед данными кадра?

Это протокол Engine.io, где числа — это кодировка пакета:

[]

  • 0 open - отправляется с сервера при открытии нового транспорта (перепроверено)

  • 1 close — Запрос на закрытие этого транспорта, но не самого соединения.

  • 2 пинга - отправил клиент. Сервер должен ответить пакетом pong, содержащим те же данные.

    Клиент отправляет: тестовый кадр 2probe

  • 3 pong — отправляется сервером в ответ на пинг-пакет.

    Сервер отправляет: 3probe, отвечает клиенту

  • 4 сообщение - фактическое сообщение, клиент и сервер должны вызывать свои обратные вызовы с данными.

  • Обновление 5. Прежде чем Engine.io переключает транспорты, он проверяет, могут ли сервер и клиент обмениваться данными через этот транспорт. Если этот тест проходит успешно, клиент отправляет пакет обновления с просьбой к серверу сбросить кэш на старом транспорте и переключиться на новый транспорт.

  • 6 noop - пакет noop. В основном используется для принудительного цикла опроса при получении входящего соединения WebSocket.

Пример

发送数据

接收数据

Приведенный выше снимок экрана является примером передачи данных в приведенном выше примере, и общий процесс выглядит следующим образом:

  1. подключить рукопожатие успешно
  2. Клиент отправит 2 тестовых кадра
  3. Сервер отправляет кадр ответа 3probe
  4. Клиент отправит кадр обновления с содержимым 5
  5. Сервер отвечает на кадр noop с содержимым 6
  6. После прохождения проверки детектирования клиент останавливает опрос, переводит канал передачи на вебсокет соединение, а после перехода на вебсокет начинает периодически пинг/понг (по умолчанию 25 секунд).
  7. Клиент и сервер отправляют и получают данные, 4 представляет сообщение сообщения engine.io, за которым следует содержимое отправленного и полученного сообщения.

Чтобы узнать, является ли соединение между клиентом и сервером нормальным, ClientSocket и ServerSocket, используемые в проекте, имеют поток сердцебиения. Этот поток в основном предназначен для определения того, нормально ли подключены клиент и сервер. в основном гарантируется процессом пинг-понга.

Интервал, с которым периодически отправляется пульс, составляет 25 м, установленный по умолчанию socket.io, что также можно наблюдать на рисунке выше. Этот интервал можно пройти черезнастроитьИсправлять.

socket通信流程

Ссылаться наengine.io-protocol

Справочная статья

Краткий обзор технологии веб-передачи в реальном времени Подробное объяснение принципа engine.io

Кроме того, мы недавно открыли вакансии по набору персонала, добро пожаловать! >Рекрутинг Mint Front-End

Широкая реклама

Эта статья была опубликована вЕженедельный выпуск Mint Front End, Добро пожаловать в Watch & Star ★, пожалуйста, указывайте источник при перепечатке.

Добро пожаловать, чтобы обсудить, поставить лайк и перейти 。◕‿◕。~