Socket.io 1.x: использовать только WebSockets?

Мы разрабатываем веб-приложение, которое будет работать только в современных браузерах (IE10 +) по разным причинам.

Одна из реализованных нами функций - Socket.io 1.x. Однако по умолчанию клиент Socket.io пытается поддерживать старые браузеры, поэтому он запускает соединение с длинным опросом, а затем обновляет его до WebSockets. Это пустая трата времени и ресурсов, поскольку мы точно знаем, что браузер поддерживает WS.

Я искал, и я могу найти эту страницу wiki, которая, однако, относится к Socket.io 0.9.

В конце концов я нашел документацию для engine.io-client (на которой Socket.io-клиент основан на ветке 1.x). Это код, который я написал и, кажется, работает. Тем не менее, я хотел бы знать, правильно ли он или что-то не так:

io.connect('https://...', {
    upgrade: false,
    transports: ['websocket']
})

Как ни странно, просто установить свойство transports в массив с websockets было недостаточно; Мне также пришлось отключить upgrade. Правильно ли это?

Update

Я сделал несколько новых открытий.

Если параметр transports установлен только на ['websocket'], это не имеет никакого значения, если включено upgrade или нет. Это нормально?

Ответ 1

Существует два типа "обновлений" с socket.io. Сначала (в socket.io 1.0+), socket.io запускает все подключения с помощью запроса HTTP-опроса и может фактически обменять некоторые начальные данные только с помощью HTTP-запроса. Затем, в какой-то момент после этого, он попытается инициировать соединение с webSocket. соединение webSocket выполняется путем отправки определенного типа HTTP-запроса, который указывает заголовок upgrade: websocket, и сервер может затем ответить соответствующим образом, поддерживает ли он websocket или нет. Если сервер согласен с обновлением, то это конкретное http-соединение "обновляется" до протокола webSocket. В этот момент клиент тогда знает, что поддерживается webSocket, и он перестает использовать HTTP-запросы опроса, тем самым заканчивая свой upgrade на webSocket.

Вы можете полностью исключить первоначальный HTTP-опрос, выполнив это на клиенте:

var socket = io({transports: ['websocket'], upgrade: false});

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

io.set('transports', ['websocket']);

Но если вы установите это на сервере, клиенты socket.io, которые первоначально подключаются к HTTP-опросу, вообще не работают. Таким образом, это должно быть согласовано только с правильными настройками на клиенте, чтобы клиент никогда не начинал с опроса.

Это говорит обо всех концах, что вы хотите использовать только webSockets, и socket.io будет пропустить дополнительный HTTP-опрос в начале. Справедливое предупреждение, для этого требуется поддержка webSocket, поэтому это исключает совместимость со старыми версиями IE, которые еще не поддерживают webSocket. Если вы хотите сохранить совместимость, то просто позвольте socket.io сделать это с помощью нескольких HTTP-запросов изначально.


Здесь больше информации об обновлении протокола от http до webSocket.

Протокол webSockets инициирует КАЖДЫЙ webSocket с HTTP-соединением. То, как работают все веб-камеры. Это HTTP-соединение содержит некоторые заголовки, указывающие на то, что браузер "хотел" обновиться до протокола webSockets. Если сервер поддерживает этот протокол, он отвечает, сообщая клиенту, что он будет обновляться до протокола webSocket, и тот самый сокет затем переключается с протокола HTTP на протокол webSocket. Так создается соединение webSocket. Таким образом, тот факт, что вы видите, что ваше соединение с веб-сайтом, начинающееся с HTTP-соединения, на 100% нормальное.

Вы можете настроить socket.io на НИКОГДА не использовать длительный опрос, если это заставляет вас чувствовать себя лучше, но это не изменит того факта, что соединение с WebSocket все равно начнется с HTTP-соединения, которое затем будет обновлено до протокола webSocket, и оно будет не повышают эффективность работы в современных браузерах, поддерживающих webSockets. Это, однако, сделает так, чтобы ваше соединение не работало в старых браузерах.

Ответ 2

Чтобы сообщить Socket.IO использовать WebSocket только вместо нескольких запросов XHR, просто добавьте это на сервер Node:

io.set('transports', ['websocket']);

И на клиенте добавьте это:

var socket = io({transports: ['websocket']});

Это указывает Socket.IO использовать протокол WebSocket и ничего другого; он чище, быстрее и использует немного меньше ресурсов на стороне клиента и сервера.

Теперь вы увидите только одно соединение WebSocket в списке сетевых запросов, просто имейте в виду, что IE9 и ранее не могут использовать WebSocket.

Ответ 3

Я отправляю этот ответ, потому что принятый ответ неверен - он смущает обновление Socket.IO от длительного опроса AJAX до WebSocket с помощью запроса WSS "Connection: Upgrade". Проблема не в том, что соединение WebSocket начинается с HTTP и обновляется до WebSocket - как это могло быть? - но Socket.IO начинается с соединения AJAX с длинным опросом даже в браузерах, поддерживающих WebSocket, и только обновляет их позже, после обмена некоторыми трафиком. Это очень легко увидеть в инструментах разработчика Firefox или Chrome.

Автор вопроса верен в своих наблюдениях. "Обновление" в Socket.IO не относится к обновлению протокола HTTP-WSS, как это часто неправильно понимается, но к обновлению соединения Socket.IO от длительного соединения AJAX-соединения с WebSocket. Если вы начинаете работу с WebSocket уже (это не по умолчанию), то обновление false не влияет, потому что вам не нужно обновлять. Если вы начинаете с опроса и отключите обновление, то он остается таким образом и не обновляется до WebSocket.

См. ответы arnold и Nick Steele, если вы не хотите начинать с долгого опроса. Я объясню, что происходит более подробно.

Это то, что я наблюдал в моих экспериментах с помощью простых приложений WebSocket и Socket.IO:

WebSocket

2 запроса, 1,50 КБ, 0,05 с

Из этих двух запросов:

  • Сама HTML-страница
  • обновление подключения к WebSocket

(Запрос обновления соединения виден в инструментах разработчика с ответом 101 "Ответы на переключение" ).

Socket.IO

6 запросов, 181,56 КБ, 0,25 с

Из этих 6 запросов:

  • сама страница HTML
  • Socket.IO JavaScript (180 килобайт)
  • запрос на длинный опрос AJAX
  • второй длинный запрос AJAX запроса
  • третий длинный запрос AJAX для опроса
  • обновление подключения к WebSocket

Подробнее

Результаты WebSocket, которые я получил на localhost:

Результаты WebSocket - websocket-vs-socket.io module

Результаты Socket.IO, которые я получил на localhost:

Результаты Socket.IO - websocket-vs-socket.io module

Проверьте себя

Я опубликовал код на npm и на GitHub, вы можете запустить его самостоятельно:

# Install:
npm i -g websocket-vs-socket.io
# Run the server:
websocket-vs-socket.io

и следуйте указаниям. Чтобы удалить:

# Uninstall:
npm rm -g websocket-vs-socket.io

Подробнее см. этот ответ.

Ответ 4

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

var url = serverUrl + "/ssClients"  //ssClients is the socket.io namespace

var connectionOptions =  {
    "force new connection" : true,
    "reconnection": true,
    "reconnectionDelay": 2000,                  //starts with 2 secs delay, then 4, 6, 8, until 60 where it stays forever until it reconnects
    "reconnectionDelayMax" : 60000,             //1 minute maximum delay between connections
    "reconnectionAttempts": "Infinity",         //to prevent dead clients, having the user to having to manually reconnect after a server restart.
    "timeout" : 10000,                           //before connect_error and connect_timeout are emitted.
    "transports" : ["websocket"]                //forces the transport to be only websocket. Server needs to be setup as well/
}
var socket = require("socket.io-client")(url, connectionOptions); 

socket.on("connect", function (_socket) {
    logger.info("Client connected to server: " + clientName);
    logger.info("Transport being used: " + socket.io.engine.transport.name);

    socket.emit("join", clientName, function(_socketId) {  //tell the server the client name
        logger.info("Client received acknowledgement from server: " + _socketId);
        logger.info("Transport being used after acknowledgement: " + socket.io.engine.transport.name);

    });
});

После установки сервера вы увидите следующее:

2015-10-23T19:04:30.076Z - info:    Client connected to server: someClientId 
2015-10-23T19:04:30.077Z - info:    Transport being used: websocket 
2015-10-23T19:04:30.081Z - info:    Client received acknowledgement from server: aMH0SmW8CbiL8w5RAAAA
2015-10-23T19:04:30.081Z - info:    Transport being used after acknowledgement: websocket

Если вы не навязываете транспорт, вы увидите "опрос" вместо websocket. Однако этого не происходит на стороне клиента, сервер также должен быть настроен:

var io = require("socket.io")(server, { adapter: adapter, log: false }); //attach io to existing Express (http) server
..
io.set('transports', ['websocket']); //forces client to connect as websockets. If client tries xhr polling, it won't connect.

Опасность

Если клиент фактически не поддерживает протокол websocket, соединение не произойдет, и клиент сообщит xhr poll error.

Это отлично работает для меня, потому что я могу контролировать клиентов, которых у меня есть, поэтому у меня есть роскошь, чтобы заставить веб-сайты сразу же, и я считаю, что задает оригинальный вопрос. Я надеюсь, что это поможет кому-то там...