Socket.io: Двойное соединение

Созданный на 23 авг. 2011  ·  42Комментарии  ·  Источник: socketio/socket.io

каким-то образом двойное соединение происходит с клиентами (что означает, что все обработчики вызываются дважды), не мог воспроизвести ситуацию самостоятельно, но продолжал получать отчеты об ошибках, поведение точно такое же, если вы вызываете socket.socket.connect () на уже подключенном socket. (проверено на websocket и flashsocket)

Socket.IO client bug

Самый полезный комментарий

Я все еще сталкиваюсь с этой проблемой согласно базовому примеру.

РЕДАКТИРОВАТЬ: Я нашел исправление, хотя я не уверен, что это точно. По сути, все, что для этого требуется, - это использовать

io.once('connection', ...)

вместо

io.on('connection', ...)

Все 42 Комментарий

в качестве быстрого взлома вы можете использовать "принудительное новое соединение": ложная конфигурация

Я тоже заметил эту ошибку в нашем приложении.
Следующим было воспроизведение. В FF с подключением к веб-сокету произошла ошибка. Затем одно за другим сгенерировались 3 события переподключения. После этого было установлено 3 новых подключения, и браузер получил 3 сообщения вместо одного.

Может какая то ошибка в логике переподключения? То, что генерирует несколько переподключений вместо одного.

Я видел проблемы в старых браузерах, которые не поддерживают веб-сокеты (например, FF 3.6), где инициализация socket.io в событии готовности dom ("$ ()" в jQuery) вызывает несколько соединений, тогда как инициализация в более позднем window.load событие нет. Я обнаружил, что эта конкретная проблема постоянно воспроизводима. Не уверен, что это то же самое, что вы видите, но симптомы очень похожи.

Та же проблема и здесь. Проблема в том, что если сервер по какой-либо причине выходит из строя, а затем снова включается, повторное подключение приводит к n + 1 ответу каждый раз. Итак, если мы скажем, что 3 сбоя сервера, у нас будет 4 ответа на каждый сервер. Обновление страницы решает проблему. Кто-нибудь нашел решение для этого [даже если это временно?]

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

me.socket.on('connect', function () {
});

me.socket.on('message', function(data) {

});

me.socket.on('disconnect', function() {

});

После недели отладки с помощью перехваченного дампа Wireshark мне удалось найти причину и воспроизвести эту ошибку.

Короче говоря, логика переподключения очень хрупкая. Это зависит от множества таймеров, которые могут работать параллельно, и это приводит к нескольким переподключениям. Эта строка https://github.com/LearnBoost/socket.io-client/blob/master/lib/socket.js#L511 является основной причиной этой ошибки.

Теперь полностью воспроизведите:

  1. Подключитесь от клиента к серверу.
  2. Поместите медленный код на свой сервер (мы должны приостановить основной цикл node.js на 4-8 секунд) через факториал:
    функция fibo (n) {
    если (n <2) вернуть 1;
    вернуть fibo (n-2) + fibo (n-1);
    }
    Поместите его в auth-section. Это должно быть вызвано рукопожатием:
    io.set ('авторизация', функция (данные, принять) {
    console.info ("Fibo ::" + fibo (41));
    Вы должны поэкспериментировать в консоли узла, чтобы найти fibo (N), который
    заблокирует основной цикл на 4-8 секунд.
  3. Теперь вам нужно быстро перезапустить свой сервер.
    Для подключения будет применяться медленный код.
    Клиент должен быть уведомлен о том, что это не рукопожатие.
    А теперь пытается переподключиться.
    В консоли вы увидите несколько попыток переподключения (если вы поставили логирование на обратные вызовы «переподключение / переподключение»).
    После нашего взлома замедления он будет повторяться вечно.

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

Эта строка (socket.js, 511):
self.reconnectionTimer = setTimeout (возможно, Reconnect, self.reconnectionDelay);

планирует событие после вызова соединения. И если ответ на соединение задерживается как минимум на одну секунду, он срабатывает и помещает новое повторное соединение в очередь. Через 2 секунды он добавляет еще один и так один.

Я исправил, но он не тестировался и выглядит не очень солидно. Основная проблема - как понять, какой обратный вызов / таймер можно запускать одновременно с функцией MaybeReconnect.
Эта строка: https://github.com/LearnBoost/socket.io-client/blob/master/lib/socket.js#L490 также может привести к дублированию соединения.

О клиенте Socket.io будет намного проще думать, если он будет реорганизован для использования некоторого State Machine. В настоящее время состояние распределяется по различным флагам (подключен, подключен, переподключен и т. Д.). И во многих функциях я видел такие охранники, как «if (! Self.reconnecting)» и многие другие.
Конечный автомат может упростить эту логику, чтобы иметь набор состояний и событий. А таймеры можно использовать только для запуска события. Если это событие попадает в неправильное состояние, конечный автомат может просто проигнорировать это событие и не беспокоиться.

Я не нашел хорошего STM для JS, но этот из Ruby можно легко перенести на JS: https://github.com/geekq/workflow

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

Кто-нибудь знает, как это попадает в список приоритетов разработчиков?

Ах. Похоже, эта проблема была назначена 3-му Эдену из выпуска: https://github.com/LearnBoost/socket.io/issues/430

Я сделал исправление для клиента и отправил запрос на перенос. Он хорошо работает в 0.8.4 и может хорошо работать в других версиях. Но для этого требуется отключить в источниках возможность использовать AJAX (или CORS) для рукопожатия. См. Это: https://github.com/LearnBoost/socket.io-client/pull/342 для подробностей.

Извините за возрождение такого старого вопроса. Я использую, по-моему, последнюю версию socket.io (или, по крайней мере, я установил socket.io с помощью npm). Эта проблема все еще возникает у меня, я относительно новичок в socket.io и node.js в целом. У меня также были проблемы, когда иногда первое соединение (из двух, которые у меня были только в один момент времени) было ошибкой чтения. Если я прав, говоря cris, ваше исправление было зафиксировано, поэтому этого больше не должно происходить, но оно все еще происходит, если я не пропустил важный фактор?

Редактировать -

Кажется, что он разветвляется socket.io и исправил это, и исправление не было зафиксировано в исходном socket.io?

@JTallis У меня была та же проблема, что и у @gdiz, и его предложение сработало. Если ваша проблема похожа, я предлагаю вам попробовать и сообщить нам, как это работает.

У меня также есть эта проблема с 0.9.16, которая, как мне кажется, является самой последней версией. Как работа с gdiz предотвращает это? Не совсем понял.

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

используя 0.9.16, я могу подтвердить эту проблему

Я могу получить событие двойного подключения по желанию со следующим кодом клиента и сервера в том же файле node.js:

"use strict";
var server = require('socket.io');
var client = require('socket.io-client');

setTimeout(function () {
    var io = server.listen(8888);

    io.of('/chat').on('connection', function (socket) {
        console.log('Server: /chat connection');
    });

    io.sockets.on('connection', function (socket) {
        console.log('Server: connection');
    });
}, 2000);

var socketAddress = 'http://localhost:8888/chat';
var socket = client.connect(socketAddress);

socket.on('connect', function () {
    console.log("Client: connect");
});

socket.on('error', function () {
    console.log("Client: error");
    socket.socket.reconnect();
});

Пара странностей:

1) Если я изменю соединение с пространством имен на нормальное, удалив "/ chat" из URL-адреса, тогда будет только одно событие соединения.

2) Если я запускаю сервер немедленно, изменяю время setInterval на ноль, нет начальной ошибки соединения и только одно событие соединения.

Какие-нибудь обходные пути или исправления для этого?

Имея ту же проблему. Если сервер socket.io неожиданно перезапускается, клиент повторно подключается дважды, поэтому каждое сообщение затем дважды обрабатывается клиентом. На самом деле сбивает мои подсчеты на стороне клиента.

У меня такая же проблема с выпуском 1.0.0-pre2. Когда я перезапускаю socket.io, я получаю два сообщения «400 Bad Request».

Посмотрим на это сегодня!

Посмотрим на это сегодня!

Не сомневайтесь, если вам нужна дополнительная информация, журналы или экраны! Не каждый раз.

в клиентском socket.io.js на 1.0.6 в строке 2755:

Request.prototype.create = function (isBinary, supportsBinary) {
var xhr = this.xhr = new XMLHttpRequest ({агент: this.agent, xdomain: this.xd});
...
}

Я считаю хорошей идеей установить:
xhr.timeout = экземпляр Manager._timeout - 10 мс
Таким образом вы предотвратите создание нескольких клиентских сокетов на стороне клиента.
На стороне сервера у нескольких сокетов будет тайм-аут пульса.

В базовом примере socket.io «Начало работы» (http://socket.io/get-started/chat/) есть эта проблема с двойным соединением сокета.

Одно из следующих (в порядке возрастания вероятности) приводит к соединению с двойным сокетом из соединения с одной вкладкой браузера:
а) При подключении к localhost: 3000 из самой первой вкладки браузера
б) При подключении к localhost: 3000, из второй вкладки браузера
c) Не закрывайте консоль браузера перед подключением к (localhost: 3000)

Дополнительные наблюдения:

 io.on('connection', function(socket){
    console.log('a user connected: ' + socket.id);
    socket.on('disconnect', function(){
       console.log('a user disconnected');
       console.log(socket.nickname + ' has disconnected from the chat.');
    });
});
  1. Есть два разных socket.id возникающих из одного запроса вкладки браузера.
  2. «Дублирующее» соединение сокета само отключается примерно через минуту, когда в console.log указано «undefined отключился от чата».
  3. Используя экспресс-генератор 4.2 (с использованием Morgan) и затем реализуя пример «Начало работы», «хорошие» соединения браузера приводят к созданию экспресс-журналов одной строки, например, «GET / 304 1 мс». Но всякий раз, когда есть это "двойное" соединение сокета, есть два экспресс-журнала: "GET / 304 1 мс" И "GET / 200 3 мс - 386b".

Я использую Mac, Chrome, Express 4.2 и последнюю версию socket.io.

Между созданием двух журналов существует разница во времени. Первый журнал запускается, когда Chrome автоматически заполняет мое «lo» до « localhost: 3000 », а второй журнал запускается, когда я нажимаю кнопку ввода.

У меня было сообщение журнала консоли «X пользователей подключено», и меня очень раздражало, когда мой собственный браузер запускал 2 или 3 уведомления о подключении пользователей.

Теперь я должен признаться, что мое решение проблемы заключалось в закомментировании строки console.log. Но продолжайте в том же духе, ребята.

Я все еще вижу эту проблему в последней версии. Есть обновления по этому поводу?

Я вижу эту проблему и с 1.0

Вообще не могу воспроизвести. Может кто-нибудь выложить полный пример?

14 ноября 2014 г., 2:44:50 Рекс Пехлер [email protected]
написал:

Я вижу эту проблему и с 1.0

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/Automattic/socket.io/issues/474#issuecomment -62934229
.

Я посмотрю, смогу ли я завтра придумать простой пример.

Я могу воспроизвести это. Я использую socket.io 1.3.5 и выражаю 4.12.4.

Мое экспресс-приложение находится на 127.0.0.1:3000, я открываю свой браузер и набираю этот IP-адрес, а socket.io открывает только один веб-узел.

У меня есть домен, например abc.com, и я перенаправляю его на 127.0.0.1. Я открываю abc.com в своем браузере, и в файле index.html есть строка;

<script>
        var socket = io('http://localhost:3000/mysql');

        socket.on('processlist', function (data){
            console.log(data);
        });
</script>

это открывает 2 веб-узла.

Я еще не пробовал nginx с прокси. Я дам тебе знать, как только попробую.

screen shot 2015-05-29 at 23 53 29

Проблема все еще возникает. Для меня это произошло при перезапуске сервера - я прикреплял свои обработчики событий socket.on ('message'), когда запускалось событие начального подключения с сервера, и если сервер перезагружался, пока клиент был открыт, это событие запускалось несколько раз (больше времени дольше клиент ждал перезапуска сервера).

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

client.socketEventsAttached = false;

socket.on('connect',function(){
     if (!client.socketEventsAttached){
          attachSocketEvents();
     }
});

Вы также можете разместить прослушиватели событий сообщений вне прослушивателя socket.on ('connect'), как предлагает @gdiz . У меня только что возникли проблемы с запуском событий сокетов до того, как сервер или клиент будут готовы к ним.

@bbcollinsworth что такое клиентский объект?

Все еще сталкиваюсь с этой проблемой со свежим socketio и основным примером.

Я все еще сталкиваюсь с этой проблемой согласно базовому примеру.

РЕДАКТИРОВАТЬ: Я нашел исправление, хотя я не уверен, что это точно. По сути, все, что для этого требуется, - это использовать

io.once('connection', ...)

вместо

io.on('connection', ...)

@brandonraphael, не могли бы вы представить пример, воспроизводящий вашу проблему (например, на основе https://github.com/darrachequesne/socket.io-fiddle) и открыть новую проблему?

Любое обновление все еще происходит и создает проблемы в приложении.

Это журналы -
0 | Сервер Api | Связаны!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DKap3hUYFSpKBRr7AFgc 4351 26.12.2018 10:58:25
0 | Сервер Api | Связаны!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VS98DBFVTNF6ifzmAFgd 4351 26.12.2018 10:58:25

4351 - это идентификатор пользователя, и количество подключений также не статично, как 2 в случае для пользователя 4351.
Другой журнал показывает 6 подключений одновременно для одного и того же пользователя.

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

Любая помощь была бы действительно замечательной.
Заранее спасибо.

Я все еще сталкиваюсь с этой проблемой согласно базовому примеру.

РЕДАКТИРОВАТЬ: Я нашел исправление, хотя я не уверен, что это точно. По сути, все, что для этого требуется, - это использовать

io.once('connection', ...)

вместо

io.on('connection', ...)

Спасибо тебе за это

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

Была такая же проблема, и это сводило меня с ума. Определенно НЕ перерегистрировал слушателей событий на клиенте. В моей настройке у меня есть сервер nodejs, на котором размещена конечная точка socketio, и у меня также есть другой сервер nodejs, выступающий в качестве моего клиента socketio. Хитрость заключалась в том, чтобы соединить клиентскую сторону с транспортом websocket и использовать forceNode . Ex

// client side
const socket = io('<url>', {
  transports: ['websocket'],
  forceNode: true,
});

Я считаю, что у меня такая же проблема. Я создаю чат-комнату, и когда кто-то присоединяется к ней, я испускаю "присоединился к комнате "сообщение комнате.

Все работает отлично, но если я выйду из комнаты, а затем войду в нее во второй раз, вступительное сообщение будет отправлено дважды. Если я выйду из комнаты снова и снова войду в нее в третий раз, вступительное сообщение будет отправлено три раза и так далее. Каждое _reconnection_ к _same_ room приводит к дублированию вступительного сообщения. Однако, если я войду в _другую_ комнату, я вижу вступительное сообщение только один раз.

Использование io.once('connection', ...) не помогло мне. Фактически, все мои мероприятия перестали работать.

Использование метода «debounce» тоже не помогло. Опять же, ни одно из моих событий даже не зарегистрировано.

Наконец, использование клиентской стороны forceNode: true option тоже не работает.

Я использую 2.2.0 как на сервере, так и на клиенте. Что мне не хватает?

Меня интересовала та же проблема, но в конечном итоге помогло сообщение клиенту о подключении только once .

Вот мой конфиг для клиента:

var socket = io.connect("http://localhost:3000/test", 
    { upgrade: false, transports: ['websocket'], reconnection: true, forceNew: false});
socket.once('connect', socketConn => {
    socket.on('message', data => {
        console.log(data);
    });
}); 

Клиент просто будет зарегистрирован once для события connect , поэтому любое другое событие внутри него будет зарегистрировано один раз. Теперь, если сервер выйдет из строя, клиент просто попытается повторно подключиться к нему и не будет пытаться создать новое соединение. Как только он получит ответ от сервера, он начнет обработку сообщений.

Поэтому io.once нужно устанавливать на стороне клиента, а не на стороне сервера.

Я использую версию клиента 2.1. Я считаю, что это очень просто запустить в моем dev env. Каждый раз, когда мой сервер узла перезагружается (например, используя nodemon), клиент всегда запускает несколько событий повторного подключения, а затем даже подключается. Но я не могу понять основную причину

Я использую версию клиента 2.1. Я считаю, что это очень просто запустить в моем dev env. Каждый раз, когда мой сервер узла перезагружается (например, используя nodemon), клиент всегда запускает несколько событий повторного подключения, а затем даже подключается. Но я не могу понять основную причину

У меня была та же проблема, что и при использовании самого nodemon. но в конечном итоге, указание клиенту подключиться только once помогло.
Попробуйте код из моего ответа чуть выше. Это помогло мне, и каждый раз, когда сервер перезагружается, клиенты получают новые подключения.

Моя проблема была проста

У меня было одно и то же клиентское приложение, открытое на 2 вкладках. ой

У меня была такая же проблема. Однако я ошибочно поместил свои клиентские обработчики в функцию обратного вызова socket.on('connection', cb) .

Вот фрагмент для демонстрации:

client.js (в node.js)

const client = require('socket.io-client');
const socket = client('my_endpoint', {
  transports: [ 'websockets' ]
});

socket.on('connect', () => {
  console.log('connected');
  socket.on('myEvent', (message) => {
    console.log(`message: ${message}`);
  });
});

Когда происходит отключение и повторное подключение, он снова вызывает socket.on('connect', cb) , а обработчик myEvent регистрирует второго обработчика с тем же именем. Поэтому, когда сервер снова отправил myEvent , у меня было бы два журнала консоли с одним и тем же сообщением.

Чтобы решить эту проблему, мне пришлось поместить другие обработчики вне обработчика connect .

const client = require('socket.io-client');
const socket = client('my_endpoint', {
  transports: [ 'websockets' ]
});

socket.on('connect', () => {
  console.log('connected');
});

socket.on('myEvent', (message) => {
  console.log(`message: ${message}`);
});

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

Кроме того, хотя я видел эту проблему конкретно в node.js, я уверен, что это будет проблемой и в браузере.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги