Внедрение WebSocket и реализация функции чата с помощью node+socket-io

внешний интерфейс WebSocket

Когда я делал игру «Нарисуй и угадай», я использовал Socket.io, чтобы реализовать передачу изображений в реальном времени и функцию чата при угадывании вопросов.Эта статья в основном знакомит с тем, что такое Socket.io и как его использовать Это.

WebSocket

Прежде чем представить Socket.io, нам сначала нужно поговорить о том, что такое WebSocket.

Подробнее о ссылках:

Зачем нужны веб-сокеты

Мы знаем, что до HTML5 клиент и сервер обменивались данными по протоколу HTTP, но протокол HTTP имеет две характеристики:

  • HTTP-протокол — это односторонний сетевой протокол..在建立连接后,它只允许客户端 Browser/UA (User Agent) 向服务器 WebServer 发送请求后,WebServer 才能返回相应的数据。而 WebServer 不能主动推送数据给 Browser/UA。

  • Протокол HTTP не имеет состояния. Запрос на подключение, отправленный клиентом на сервер, будет включать идентификационную информацию (идентификационную информацию). Каждый раз, когда соединение завершается, сервер отбрасывает информацию для аутентификации. Когда клиент снова отправляет HTTP-запрос, ему необходимо повторно отправить информацию.

Теперь предположим, что нам нужно разработать веб-приложение и получить данные в реальном времени с сервера, такие как котировки акций в реальном времени, содержимое чатов в чатах и ​​т. д., что требует повторного HTTP-соединения между клиентом. и сервер, и клиент постоянно отправляют запрос на получение текущих данных в реальном времени. Два распространенных способа описаны ниже:

  • ajax-опрос
    Принцип опроса ajax очень прост, то есть позволить браузеру регулярно (каждые несколько секунд) отправлять запрос на сервер, чтобы узнать, есть ли новые данные, если есть, вернуть последние данные, и браузер отобразит последние данные после их получения, а затем повторите этот процесс.

  • Long Polling
    Принцип длительного опроса аналогичен принципу опроса ajax, оба из которых используют опрос, который является улучшением опроса. После того, как клиент отправляет запрос на сервер, сервер не сразу отвечает клиенту, а поддерживает соединение, когда есть новые данные, он возвращается к клиенту, клиент получает данные, отображает их и сразу отправляет Сделайте новый запрос к серверу и повторите процесс. Если данные сервера не обновлялись в течение длительного времени, запрос истечет по тайм-ауту через некоторое время.После того, как клиент получит сообщение о тайм-ауте, он сразу же отправляет новый запрос на сервер.

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

Ajax-опрос Если на сервере нет обновленных данных в течение определенного периода времени, но клиенту все еще необходимо регулярно отправлять запросы, сервер возвращает предыдущие старые данные, клиент получает старые данные, а затем снова отображает неизмененные данные. То есть в течение этого периода клиент и сервер будут регулярно обмениваться информацией о постоянных данных, что приводит к нерациональному использованию пропускной способности и загрузки ЦП.

Хотя Long Polling решает проблему пропускной способности и использования ЦП, если данные сервера обновляются слишком быстро, после того, как сервер возвращает пакет данных клиенту, он может отправить следующий запрос только после того, как клиент снова отправит запрос. клиент. Между сервером, возвращающим данные дважды, ему необходимо подождать, пока клиент обработает данные после получения данных, и после того, как клиент снова отправит запрос на соединение, сервер проверяет информацию аутентификации клиента и успешно устанавливает время соединения, в случае перегрузки сети Далее это должно быть неприемлемо для пользователей.

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

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

Протокол веб-сокета

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

  • WebSocket — это протокол двусторонней связи.После установления соединения как сервер WebSocket, так и браузер/UA могут активно отправлять или получать данные друг другу, точно так же, как Socket, разница в том, что WebSocket — это своего рода простая симуляция протокол сокета.

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

Рабочий процесс веб-сокета

Браузер отправляет серверу запрос на установление WebSocket-соединения через JavaScript, после чего клиент и сервер могут напрямую обмениваться данными через TCP-соединение. Поскольку соединение WebSocket по сути является TCP-соединением, оно имеет большие преимущества в производительности по сравнению с традиционными методами опроса с точки зрения стабильности передачи данных и объема передаваемых данных.

Чтобы установить соединение через WebSocket, клиентский браузер должен сначала инициировать HTTP-запрос к серверу. Этот запрос отличается от обычного HTTP-запроса и содержит дополнительную информацию в заголовке. Дополнительная информация в заголовке «Upgrade: WebSocket» указывает на то, что это протокол приложения Для обновленного HTTP-запроса сервер анализирует дополнительную информацию заголовка, а затем генерирует ответное сообщение и возвращает его клиенту.Соединение WebSocket между клиентом и сервером устанавливается, и обе стороны могут свободно передавать информацию через этот канал соединения, и это соединение будет сохраняться до тех пор, пока один из клиентов или сервер активно не закроет соединение.

Рукопожатие WebSocket

Ниже приведен пример типичного запроса отправки и ответа WebSocket:

Браузер WebSocket инициирует запрос к серверу:

GET / HTTP/1.1
Connection:Upgrade
Host:127.0.0.1:8088
Origin:null
Sec-WebSocket-Extensions:x-webkit-deflate-frame
Sec-WebSocket-Key:puVOuWb7rel6z2AVZBKnfw==
Sec-WebSocket-Version:13
Upgrade:websocket

Существуют некоторые различия между этим запросом и нормальным запросом HTTP

Upgrade: websocket
Connection: Upgrade

Указывает, что целью запроса является обновление протокола связи между клиентом и сервером с протокола HTTP на протокол WebSocket.

Sec-WebSocket-Key:
Sec-WebSocket-Extensions:
Sec-WebSocket-Version:

Браузер клиента должен предоставить серверу информацию о рукопожатии, а сервер анализирует эти заголовки. Sec-WebSocket-Key — это значение в кодировке Base64, которое случайным образом генерируется браузером, а Sec-WebSocket-Version сообщает серверу об используемом проекте Websocket (версия протокола).

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

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Server:beetle websocket server
Upgrade:WebSocket
Date:Mon, 26 Nov 2013 23:42:44 GMT
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:content-type
Sec-WebSocket-Accept:FCKgUr8c7OsDsLFeJTWrJw6WO8Q=

Upgrade: websocket
Connection: Upgrade

Сообщите клиенту, что предстоящее обновление — это протокол Websocket.

Sec-WebSocket-Accept

Это Sec-WebSocket-Key, подтвержденный сервером и зашифрованный.

Веб-сокет-сервер

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

Стоит отметить, что WebSocket может совместно использовать порт прослушивания с HTTP, то есть может выполнять задачу сокета на общедоступном порту.

Связь между WebSocket и HTTP и TCP

Как и протокол HTTP, Websocket основан на TCP, поэтому они являются как надежными протоколами. Функция отправки Websocket, вызываемых веб-разработчиками, в конечном итоге передается через интерфейс системы TCP в реализации браузера.

Протоколы WEBSOCKET и HTTP являются одним и тем же соглашением, тогда есть ли между ними какая-либо связь? Ответ да, и WebSocket передается через HTTP-протокол, как я вижу "Get/Chat HTTP/1.1", который является как раз HTTP-протоколом для "Get/Chat HTTP/1.1". Однако после установления соединения реальная фаза передачи данных не требует участия протокола HTTP.

image

WebSocket API

var ws = new WebSocket(“ws://echo.websocket.org”);
ws.onopen = function(){ws.send(“Test!”); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log(“WebSocketClosed!”);};
ws.onerror = function(evt){console.log(“WebSocketError!”);};

В приведенном выше коде JavaScript вызывается API WebSocket.

Чтобы создать объект WebSocket, вам нужно вызвать конструктор WebSocket и передать адрес сервера, к которому необходимо подключиться. URL-адреса WebSocket начинаются с ws://.

Объекты WebSocket имеют 4 события:

  • onopen: срабатывает после успешного подключения WebSocket
  • onmessage: срабатывает, когда браузер получает данные, отправленные сервером WebSocket.
  • onclose: срабатывает, когда браузер получает запрос на закрытие соединения с сервера WebSocket.
  • onerror: сбой соединения, сбой отправки или получения данных или ошибка в обработке данных.

Поддержка браузера

Desktop

Feature Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari
Version -76 support 6 No support 4.0 (2.0) No support 11.00 (disabled) 5.0.1
Protocol version 7 support No support No support 6.0 (6.0)Moz No support No support No support
Protocol version 10 support 14 No support 7.0 (7.0)Moz HTML5 Labs ? ?
Standard - RFC 6455 Support 16 (Yes) 11.0 (11.0) 10 12.10 6.0
Usable in Workers (Yes) (Yes) 37.0 (37.0) ? ? ?

Mobile

Feature Android Edge Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Version -76 support ? No support ? ? ? ?
Protocol version 7 support ? No support ? ? ? ?
Protocol version 8 support (IETF draft 10) ? No support 7.0 (7.0) ? ? ?
Standard - RFC 6455 Support 4.4 (Yes) 11.0 (11.0) ? 12.10 6.0
Usable in Workers (Yes) (Yes) 37.0 (37.0) ? ? ?

Socket.io

Socket.io используется для связи в реальном времени между браузером и Node.js.

До написания этой статьи я просто пользовался Socket.io, но много о нем не знал, всегда думал, что Socket.io — это реализация протокола WebSocket. На самом деле эта точка зрения не совсем верна.

Введение в Socket.io

Socket.io — кроссплатформенный фреймворк с открытым исходным кодом для общения в реальном времени, полностью реализованный с помощью JavaScript, основанный на Node.js и поддерживающий протокол WebSocket, включающий JavaScript на стороне клиента и Node.js на стороне сервера. .

Socket.io разработан для поддержки любого браузера, любого мобильного устройства. Поддержка основных браузеров для ПК (IE, Safari, Chrome, Firefox, Opera и т. д.), мобильных браузеров (iphone Safari/ipad Safari/Android WebKit/WebOS WebKit и т. д.).

Тем не менее, протокол WebSocket является новым протоколом, представленным HTML5, и поддержка браузера для нее не идеальна. Видно, что Socket.io не может быть просто реализацией Websocket, он также поддерживает другие методы связи, как описано выше. прошедший избирательный опрос Ajax и длительный опрос. Согласно степени поддержки браузера, вы можете выбрать, какой метод использовать для связи.

Методы связи, поддерживаемые Socket.io:

  • WebSocket
  • Adobe Flash Socket
  • AJAX long-polling
  • AJAX multipart streaming
  • Forever IFrame
  • JSONP polling

Использование Socket.io

Сторона узла использует экспресс-фреймворк

представлять

Сервис-терминал:
npm install --save socket.io

Браузер (введение локального файла):
<script src="/socket.io/socket.io.js"></script>

Конец браузера (ускорение CDN):
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"> </script>

создать io-сервер

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
});
server.listen(3000, function() {
    console.log('App listening on port 3000!');
});

Сервер

Socket.IO предоставляет события по умолчанию (например, подключение, сообщение, отключение). Кроме того, Socket.IO позволяет отправлять и получать пользовательские события.

Слушайте клиентское соединение, функция обратного вызова будет передавать сокет этого соединения

io.on('соединение',функция(сокет){});

Рассылка сообщения всем клиентам

io.sockets.emit('Строка',данные);

Отправить пользовательское событие указанному клиенту

socket.emit('Строка', данные);
io.sockets.socket(socketid).emit('String', data);

Получение пользовательских событий, отправленных клиентом

socket.on('Строка',функция(данные));

Широковещательные сообщения клиентам, кроме себя

socket.broadcast.emit("msg", данные);

Номер

Комнаты — это очень полезная функция, предоставляемая Socket.IO. Комната эквивалентна предоставлению пространства имен для некоторых определенных клиентов, и все трансляции и коммуникации в комнате не повлияют на клиентов за пределами комнаты.

Используйте метод join() для присоединения сокета к комнате:

io.on('connection', function(socket){
    socket.on('group1', function (data) {
        socket.join('group1');
    });
    socket.on('group2',function(data){
        socket.join('group2');
    });
});

Используйте метод leave(), чтобы выйти из комнаты:

socket.leave('какая-то комната');

Отправлять сообщения в сокеты в комнате, отличные от текущего сокета

socket.broadcast.to('group1').emit('имя_события', данные);
Широковещательный метод позволяет текущему клиенту сокета не быть в группе

Отправить сообщение на все розетки в комнате

io.sockets.in ('group1') emit ('event_name', data).;

Получить подключенный клиентский сокет

io.sockets.clients().forEach(function (socket) {
    //.....
})

Получить всю информацию о комнате (группе)

io.sockets.manager.rooms

чтобы получить информацию о комнате, введенную этим сокетом

io.sockets.manager.roomClients[socket.id]

Получить клиентов в определенной комнате и вернуть все экземпляры сокетов в этой комнате

io.sockets.clients('конкретная комната')

Пространства имен

Подпрограммы могут быть установлены для Socket.IO через пространства имен. Пространство имен по умолчанию — «/», и Socket.IO по умолчанию подключается к этому пути.

Используйте функцию of() для настройки пространств имен.

var chat = io.of('/chat');
chat.on('connection', function(socket){
  console.log('someone connected');
});

клиент

Установить соединение сокета

var socket = io.connect(window.location.protocol + '//' + window.location.host);

или

var socket = io(window.location.protocol + '//' + window.location.host);

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

var chat = io.connect(window.location.protocol + '//' + window.location.host + '/chat');

Слушайте сообщения сервера

socket.on('msg',function(data){
    console.log(data);
});

socket.on("String",function(data){}) прослушивает сообщения, отправленные сервером, параметр String совпадает с первым параметром String сервера socket.emit('String', data) .

отправить сообщение на сервер

socket.emit('msg', данные);

Мониторинг отключения и повторного подключения сокета

socket.on('disconnect', function() {
    console.log("与服务器断开");
});
socket.on('reconnect', function() {
    console.log("重新连接到服务器");
});

Событие, которое Client Socket.ON () слушает

  • подключение: подключение успешно
  • подключение: подключение
  • отключить: отключить
  • connect_failed: соединение не удалось
  • error: Произошла ошибка, которая не может быть обработана другими типами событий.
  • Сообщение: так же, как событие Message Side Side
  • Anything: ЛЮБОЕ событие с сервером
  • reconnect_failed: ошибка повторного подключения
  • повторное подключение: успешное повторное подключение
  • повторное подключение: повторное подключение

чат

обработать:

  • Создать сокет-сервер
  • Браузер устанавливает сокетные соединения
  • Введите содержимое чата на странице, нажмите кнопку «Отправить» и отправьте информацию чата в пользовательское событие сокета «чат».
  • Сервер прослушивает событие «чата» браузера, и когда он получает сообщение чата от браузера, он отправляет сообщение всем браузерам, подключенным к сокету.
  • Браузер прослушивает событие «чат», отправленное сервером, и отображает его на странице, когда получает сообщение чата.

image

Код стороны узла:

var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.sockets.on('connection', function(socket) {
    // 监听客户端发送的 chat 事件
    socket.on('chat', function (chatinfo) {
        // 向当前 socket 发送聊天信息
        socket.emit('chat', chatinfo);
        // 向除了当前 socket 外的所有 socket 发送聊天信息
        socket.broadcast.emit('chat', chatinfo);
    });
});
server.listen(3000, function() {
    console.log('App listening on port 3000!');
});

HTML-код:

<div id="chat">
    <ul id="chatList">
    </ul>
    <form>
        <input type="text" name="chatContent" id="chatContent" />
        <input type="button" id="sendChatContent" value="发送" />
    </form>
</div>

Код сокета браузера:

// 建立 socket 连接
var url = window.location.protocol+'//'+window.location.host;
socket = io.connect(url);
// 点击“发送”,向服务器发送聊天信息
$('#sendChatContent').click(function (ev) {
    var username = $('#username').text();
    var chatContent = $('#chatContent').val().trim();
    if(!chatContent){
        return;
    }
    if(socket){
        // 向服务器 chat 事件,发送信息
        socket.emit('chat', {username: username, chatContent: chatContent});
    }
    $('#chatContent').val('');
});
// 监听服务器发送来的 chat 事件
socket.on('chat', function (chatinfo) {
    $('#chatList').append('<li><span class="chatusername">' + chatinfo.username + '</span>:<span class="chatcontent">' + chatinfo.chatContent + '</span></li>');
    $('#chatList').scrollTop(10000);
});