Socket.io: Conexão dupla

Criado em 23 ago. 2011  ·  42Comentários  ·  Fonte: socketio/socket.io

de alguma forma a conexão dupla acontece aos clientes (o que significa que todos os manipuladores são chamados duas vezes), não consegui reproduzir a situação por conta própria, mas continuo recebendo relatórios de erros, o comportamento é exatamente o mesmo se você chamar socket.socket.connect () em já conectado socket. (testado em websocket e flashsocket)

Socket.IO client bug

Comentários muito úteis

Ainda estou encontrando esse problema de acordo com o exemplo básico.

EDIT: Eu encontrei uma correção, embora não tenha certeza se ela é exatamente adequada. Basicamente, tudo o que é necessário é usar

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

ao invés de

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

Todos 42 comentários

como hack rápido, você pode usar "forçar nova conexão": configuração falsa

Eu também observei esse bug em nosso aplicativo.
Reproduzir foi o próximo. No FF com conexão de websocket, ocorreu algum erro. Então, um após outro 3 eventos de reconexão foram gerados. Após o estabelecimento de 3 novas conexões, o navegador recebeu 3 mensagens em vez de uma.

Talvez algum bug na lógica de reconexão? Algo que gera várias reconexões em vez de uma.

Já vi problemas em navegadores mais antigos que não suportam websockets (por exemplo, FF 3.6), onde a inicialização de socket.io no evento dom ready ("$ ()" em jQuery) causa múltiplas conexões, enquanto a inicialização no window.load posterior evento não. Descobri que esse problema específico pode ser reproduzido de maneira consistente. Não tenho certeza se é a mesma coisa que você está vendo, mas os sintomas são muito semelhantes.

O mesmo problema aqui também. O problema é que, se o servidor travar por qualquer motivo e for ligado novamente, a reconexão resultará em n + 1 respostas todas as vezes. portanto, se tivermos, digamos, 3 travamentos de servidor, teremos 4 respostas em cada emissão de servidor. Atualizar a página resolve o problema. Alguém encontrou uma solução para isso [mesmo que seja temporário?]

Esta pode não ser uma solução permanente, mas reorganizei meu código para que cada evento seja vinculado de forma independente e o problema de duplicação pareça ter sido resolvido na reconexão automática

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

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

});

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

});

Depois de uma semana de depuração por meio de despejo Wireshark detectado, consegui encontrar o motivo e reproduzir este bug.

Resumindo, a lógica de reconexão é muito frágil. Depende de muitos temporizadores que podem funcionar em paralelo e leva a várias reconexões. Esta linha https://github.com/LearnBoost/socket.io-client/blob/master/lib/socket.js#L511 é a principal razão para este bug.

Agora reproduza integralmente:

  1. Conecte-se com o cliente ao servidor.
  2. Coloque um código lento em seu servidor (devemos suspender o mainloop node.js por 4-8 segundos) por meio de fatorial:
    function fibo (n) {
    se (n <2) retornar 1;
    retorno fibo (n-2) + fibo (n-1);
    }
    Coloque-o na seção de autenticação. Deve ser chamado no aperto de mão:
    io.set ('autorização', função (dados, aceitar) {
    console.info ("Fibo ::" + fibo (41));
    Você deve experimentar em seu console de nó, para encontrar fibo (N), que
    bloqueará o loop principal por 4-8 segundos.
  3. Agora, você deve reiniciar rapidamente o seu servidor.
    Código lento será aplicado para conexão.
    O cliente deve ser notificado de que não está em um handshaking.
    E agora ele tenta se reconectar.
    No console, você verá várias tentativas de reconexão (se você colocar o logon em callbacks de "reconexão / reconexão").
    Após nosso hack de desaceleração, ele será repetido para sempre.

Na vida real, isso é reproduzido pela lentidão da resposta do servidor por pelo menos alguns segundos.
Isso pode ser quando node.js está sob carga pesada e responde com algum atraso.
Ou alguma lentidão da rede também pode levar a esse comportamento.
Ele também pode ser reproduzido após a reinicialização do servidor.

Esta linha (socket.js, 511):
self.reconnectionTimer = setTimeout (MaybeReconnect, self.reconnectionDelay);

agenda o evento após conectar a chamada. E se a resposta de conexão for atrasada por pelo menos um segundo, ela será disparada e colocará uma nova reconexão na fila. Após 2 segundos, adiciona outro e assim um.

Fiz uma correção, mas não foi testado e não parece muito sólido. Problema principal - como raciocinar sobre qual retorno de chamada / temporizador pode ser executado simultaneamente para a função MaybeReconnect.
Esta linha: https://github.com/LearnBoost/socket.io-client/blob/master/lib/socket.js#L490 também pode levar a conexão duplicada.

O cliente Socket.io pode ser muito mais simples de raciocinar se for refatorado para usar alguma máquina de estado. Atualmente, o estado é distribuído por diferentes sinalizadores (conectado, conectando, reconectando, etc.). E em muitas funções vi guardas como "if (! Self.reconnecting)" e muitos outros.
A máquina de estado pode simplificar essa lógica para ter um conjunto de estados e eventos. E os temporizadores podem ser usados ​​apenas para disparar eventos. Se esse evento atingir o estado incorreto, a máquina de estados pode simplesmente ignorar esse evento e não se incomodar.

Não encontrei um bom STM para JS, mas este de Ruby pode ser facilmente transferido para JS: https://github.com/geekq/workflow

Definitivamente +1 em consertar este problema. Tenho trabalhado para tentar encontrar uma solução para este problema sem modificar o socket.io ou socket.io-client diretamente, mas infelizmente o único método que encontrei de forma confiável é apenas desabilitar a reconexão. O que definitivamente não é uma boa solução, especialmente com o aumento do uso de dispositivos móveis, reconectar é um grande requisito.

Alguém tem alguma ideia de como isso cai na lista de prioridades dos desenvolvedores?

Ah. Parece que este problema foi atribuído ao 3rd-Eden a partir do problema: https://github.com/LearnBoost/socket.io/issues/430

Fiz uma correção para um cliente e enviei uma solicitação pull. Ele funciona bem no 0.8.4 e pode funcionar bem em outras versões. Mas requer a desativação da capacidade de fontes de usar AJAX (ou CORS) para handshake. Consulte: https://github.com/LearnBoost/socket.io-client/pull/342 para obter detalhes.

Desculpe por reviver um problema tão antigo. Estou usando, acredito, a versão mais recente do socket.io (ou pelo menos, instalei npm o socket.io). Esse problema ainda ocorre para mim, sou relativamente novo em socket.io e node.js como um todo. Também tive problemas em que, às vezes, a primeira conexão (das duas que só aconteciam uma vez) apresentava um erro de leitura. Se eu estiver correto em dizer cris, sua correção foi confirmada, então não deveria acontecer mais, mas ainda acontece, a menos que eu tenha esquecido um fator importante?

Editar -

Parece cris forked socket.io e fez uma correção nisso e a correção não foi confirmada para o socket.io original?

@JTallis Eu tive o mesmo problema que @gdiz e sua sugestão funcionou bem. Se o seu problema for semelhante, sugiro que você experimente e nos diga como funciona.

Também estou tendo esse problema com a 0.9.16, que acredito ser a versão mais recente. Como o gdiz evita que isso ocorra? Não entendi completamente.

omg ... eu gasto várias horas para descobrir o que há de errado com meu aplicativo e por que as mensagens entre o servidor e o cliente são duplicadas após a conexão perdida e renovada ...

usando 0.9.16 eu posso confirmar este problema

Posso obter um evento de conexão dupla à vontade com o seguinte código de cliente e servidor no mesmo arquivo 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();
});

Algumas coisas estranhas:

1) Se eu alterar a conexão com namespace para normal removendo o "/ chat" da url, haverá apenas um evento de conexão.

2) Se eu iniciar o servidor imediatamente, altere o tempo setInterval para zero, não há erro de conexão inicial e apenas um evento de conexão.

Qualquer solução alternativa ou correção para isso?

Tendo o mesmo problema. Se o servidor socket.io reiniciar inesperadamente, o cliente se reconecta duas vezes, de forma que cada mensagem é processada duas vezes pelo cliente. Realmente confunde minhas contas no lado do cliente.

Eu tenho o mesmo problema com a versão 1.0.0-pre2. Eu tenho dois "400 Bad Request" quando coloco novamente o socket.io.

Examinarei isso hoje!

Examinarei isso hoje!

Não hesite se precisar de mais detalhes, logs ou telas! Não é sempre.

no cliente socket.io.js em 1.0.6 na linha 2755:

Request.prototype.create = function (isBinary, supportedBinary) {
var xhr = this.xhr = new XMLHttpRequest ({agente: this.agent, xdomain: this.xd});
...
}

Acredito que seja uma boa ideia definir:
xhr.timeout = instância do Manager._timeout - 10 ms
Desta forma, você evita a criação de múltiplos soquetes de cliente no lado do cliente.
No lado do servidor, vários sockets terão tempo limite de pulsação.

O exemplo básico de socket.io "Getting Started" (http://socket.io/get-started/chat/) tem este problema de conexão de socket duplo.

Um dos seguintes (em ordem crescente de probabilidade) resulta na conexão de soquete duplo de uma única conexão de guia do navegador:
a) Ao conectar-se ao host local: 3000, na própria guia do navegador
b) Ao conectar-se ao localhost: 3000, a partir de uma segunda guia do navegador
c) Manter o console do navegador aberto, antes de se conectar a (localhost: 3000)

Observações adicionais:

 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. Existem dois socket.id distintos provenientes de uma única solicitação de guia do navegador.
  2. A conexão de soquete "duplicado" se desconecta, em aproximadamente um minuto, com o console.log de "undefined desconectou-se do bate-papo."
  3. Usando o gerador expresso 4.2 (usando Morgan) e implementando o exemplo "Getting Started", as "boas" conexões do navegador resultam em logs expressos de uma única linha, por exemplo, "GET / 304 1ms". Mas sempre que houver essa conexão de soquete "duplo", há dois registros expressos, "GET / 304 1ms" E "GET / 200 3ms - 386b"

Estou usando Mac, Chrome, Express 4.2 e o socket.io mais recente.

Há uma diferença de tempo entre a criação dos dois logs. O primeiro log é disparado quando o Chrome preenche automaticamente meu "lo" para " localhost: 3000 " e o segundo log é disparado quando eu clico no botão Enter.

Eu recebi uma mensagem de log do console "X usuários conectados" e fiquei muito chato quando meu próprio navegador acionou 2 ou 3 avisos de conexão do usuário.

Agora, devo confessar que minha solução para o problema foi comentar a linha console.log. Mas continuem com o bom trabalho, pessoal.

Ainda vejo esse problema na versão mais recente. Alguma atualização sobre isso?

Também estou vendo esse problema com 1.0

Não é possível reproduzir de forma alguma. Alguém pode postar um exemplo completo?

Na sexta-feira, 14 de novembro de 2014 às 2:44:50, Rex Pechler [email protected]
escrevi:

Também estou vendo esse problema com 1.0

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/Automattic/socket.io/issues/474#issuecomment -62934229
.

Vou ver se consigo preparar um exemplo simples amanhã.

Posso reproduzi-lo. Estou usando socket.io 1.3.5 e express 4.12.4.

Meu aplicativo expresso fica em 127.0.0.1:3000 e eu abro meu navegador e digito esse endereço IP e socket.io abre apenas um websocket.

Eu tenho um domínio como abc.com e o encaminho para 127.0.0.1. Abro abc.com no meu navegador e no arquivo index.html há a linha;

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

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

isso abre 2 websockets.

Ainda não experimentei o nginx com proxy. Eu vou deixar você saber assim que eu tentar.

screen shot 2015-05-29 at 23 53 29

Ainda encontrando esse problema. Para mim, aconteceu na reinicialização do servidor - eu estava conectando meus manipuladores de eventos socket.on ('mensagem') quando o evento de conexão inicial do servidor disparou, e se o servidor reiniciou enquanto o cliente estava aberto, esse evento disparou várias vezes (mais tempo o mais tempo o cliente estava aguardando a reinicialização do servidor)

Minha correção por agora tem sido 'debounce' o evento de soquete anexado para que isso aconteça apenas na primeira conexão do servidor, assim:

client.socketEventsAttached = false;

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

Você também pode colocar seus ouvintes de evento de mensagem fora do ouvinte socket.on ('conectar') como @gdiz sugeriu. Acabei de ter alguns problemas com eventos de soquete disparando antes de o servidor ou cliente estar pronto para eles.

@bbcollinsworth qual é o objeto cliente?

Ainda encontrando esse problema com o novo socketio e o exemplo básico.

Ainda estou encontrando esse problema de acordo com o exemplo básico.

EDIT: Eu encontrei uma correção, embora não tenha certeza se ela é exatamente adequada. Basicamente, tudo o que é necessário é usar

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

ao invés de

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

@brandonraphael poderia fornecer um exemplo reproduzindo o seu problema (com base em https://github.com/darrachequesne/socket.io-fiddle, por exemplo) e abrir um novo problema?

Qualquer atualização isso ainda está acontecendo e criando problemas no aplicativo

Estes são os logs -
0 | Servidor Api | Conectado!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DKap3hUYFSpKBRr7AFgc 4351 2018-12-26 10:58:25
0 | Servidor Api | Conectado!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VS98DBFVTNF6ifzmAFgd 4351 2018-12-26 10:58:25

4351 é o ID do usuário e o número de conexões também não é estático como 2 no caso do usuário 4351.
Outro log mostra 6 conexões ao mesmo tempo para o mesmo usuário.

Além disso, verifiquei esses dois IDs de soquete no servidor e eles estão se mostrando válidos. Mas o frontend só consegue escutar um deles, que é sempre o primeiro id de soquete.

Qualquer ajuda seria muito boa.
Desde já, obrigado.

Ainda estou encontrando esse problema de acordo com o exemplo básico.

EDIT: Eu encontrei uma correção, embora não tenha certeza se ela é exatamente adequada. Basicamente, tudo o que é necessário é usar

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

ao invés de

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

Obrigado por isso

O problema foi resolvido após alterar a versão usada no cliente. Outra correção seria usar salas em vez de id de soquete único. Descobri que cada ID de soquete é em si uma sala, a única parte que deveria ser alterada era do novo código de conexão de colocar cada ID de soquete na sala do usuário.
Considere que um usuário slug é o Usuário1, então uma sala foi criada Usuário1 e cada nova conexão de soquete via slug Usuário1 foi colocada dentro da sala Usuário1.

Estava tendo o mesmo problema e isso estava me deixando louco. Definitivamente NÃO estava registrando novamente os ouvintes de eventos no cliente. Em minha configuração, tenho um servidor nodejs hospedando o endpoint socketio e também tenho outro servidor nodejs atuando como meu cliente socketio. O truque era conectar o lado do cliente com o transporte do websocket e usar forceNode . Ex

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

Eu acredito que estou tendo o mesmo problema. Estou construindo uma sala de chat e quando alguém entra na sala, emito um "juntou-se à sala "mensagem para a sala.

Tudo funciona bem, mas se eu sair da sala e entrar nela pela segunda vez, a mensagem de introdução é emitida _duas vezes_. Se eu sair da sala novamente e entrar novamente pela terceira vez, a mensagem de introdução será emitida três vezes e assim por diante. Cada _reconexão_ à _mesma_ sala resulta em uma mensagem de introdução duplicada. No entanto, se eu entrar em uma sala _diferente_, só vejo a mensagem de introdução uma vez.

Usar io.once('connection', ...) não funcionou para mim. Na verdade, todos os meus eventos pararam de funcionar.

Usar o método "debounce" também não funcionou. Novamente, nenhum dos meus eventos sequer foi registrado.

Por último, usar forceNode: true option lado do cliente também não funcionou.

Estou usando 2.2.0 no servidor e no cliente. Está faltando alguma coisa?

Eu estava me perguntando sobre o mesmo problema, mas, no final das contas, dizer ao cliente para se conectar apenas once ajudou.

Esta é minha configuração para o cliente:

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);
    });
}); 

O cliente apenas será registrado once para o evento connect , portanto, qualquer outro evento dentro dele seria registrado uma vez. Agora, se o servidor travar, o cliente apenas tentará se reconectar a ele e não tentará criar uma nova conexão. Assim que obtiver a resposta do servidor, ele começará a processar as mensagens.

Portanto, io.once precisa ser definido no lado do cliente, não no lado do servidor.

Estou usando a versão 2.1 do cliente. Acho que é muito fácil acioná-lo no meu env dev. Cada vez que meu servidor de nó é reiniciado (por exemplo, usando o nodemon), o cliente sempre aciona vários eventos de reconexão e até mesmo conecta. Mas eu não consigo descobrir a causa raiz

Estou usando a versão 2.1 do cliente. Acho que é muito fácil acioná-lo no meu env dev. Cada vez que meu servidor de nó é reiniciado (por exemplo, usando o nodemon), o cliente sempre aciona vários eventos de reconexão e até mesmo conecta. Mas eu não consigo descobrir a causa raiz

Eu estava tendo o mesmo problema que também usando o próprio nodemon. mas no final das contas, dizer ao cliente para se conectar apenas once ajudou.
Experimente o código da minha resposta acima. Isso me ajudou e cada vez que o servidor é reiniciado, os clientes estão obtendo novas conexões.

Meu problema era simples

Eu tinha o mesmo aplicativo do lado do cliente aberto em 2 guias. opa

Eu tive o mesmo problema também. No entanto, para mim, eu coloquei por engano meus manipuladores do lado do cliente dentro da função de retorno de chamada socket.on('connection', cb) .

Aqui está um snippet para demonstrar:

client.js (em 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}`);
  });
});

Quando uma desconexão e reconexão acontecessem, ele chamaria socket.on('connect', cb) novamente, e o manipulador myEvent registraria um segundo manipulador com o mesmo nome. Portanto, quando o servidor emitisse myEvent novamente, eu teria dois logs de console da mesma mensagem.

Para resolver, tive que colocar meus outros manipuladores fora do manipulador 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}`);
});

Acho que minha confusão veio de como os documentos mostram o lado do servidor colocando socket.on eventos dentro do evento connect . Isso é culpa minha por não ter lido os documentos com mais cuidado, mas achei melhor colocar isso aqui para o caso de outra pessoa cometer o mesmo erro.

Além disso, embora eu tenha visto esse problema especificamente no node.js, tenho certeza de que isso também seria um problema no navegador.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

karmac2015 picture karmac2015  ·  3Comentários

adammw picture adammw  ·  4Comentários

distracteddev picture distracteddev  ·  3Comentários

varHarrie picture varHarrie  ·  3Comentários

stnwk picture stnwk  ·  4Comentários