Socket.io: RangeError: tamanho máximo da pilha de chamadas excedido

Criado em 4 jul. 2014  ·  32Comentários  ·  Fonte: socketio/socket.io

Estou apenas chamando `ìo.sockets.emit ('hey', data); and it will crash with RangeError: O tamanho máximo da pilha de chamadas excedeu . I use it in other places in my app and it works fine. I am not repeating this (checked with console). The logs say the error is in socket.io/node_modules/has-binary-data/index.js: 46``.

Não sei onde está o problema. Tentei registrar io.sockets logo antes de usá-lo e ele produziu o seguinte:

{ name: '/',
17:39:37 web.1  |   server:
17:39:37 web.1  |    { nsps: { '/': [Circular] },
17:39:37 web.1  |      _path: '/socket.io',
17:39:37 web.1  |      _serveClient: true,
17:39:37 web.1  |      _adapter: [Function: Adapter],
17:39:37 web.1  |      _origins: '*:*',
17:39:37 web.1  |      sockets: [Circular],
17:39:37 web.1  |      eio:
17:39:37 web.1  |       { clients: [Object],
17:39:37 web.1  |         clientsCount: 2,
17:39:37 web.1  |         pingTimeout: 60000,
17:39:37 web.1  |         pingInterval: 25000,
17:39:37 web.1  |         upgradeTimeout: 10000,
17:39:37 web.1  |         maxHttpBufferSize: 100000000,
17:39:37 web.1  |         transports: [Object],
17:39:37 web.1  |         allowUpgrades: true,
17:39:37 web.1  |         allowRequest: [Function],
17:39:37 web.1  |         cookie: 'io',
17:39:37 web.1  |         ws: [Object],
17:39:37 web.1  |         _events: [Object] },
17:39:37 web.1  |      engine:
17:39:37 web.1  |       { clients: [Object],
17:39:37 web.1  |         clientsCount: 2,
17:39:37 web.1  |         pingTimeout: 60000,
17:39:37 web.1  |         pingInterval: 25000,
17:39:37 web.1  |         upgradeTimeout: 10000,
17:39:37 web.1  |         maxHttpBufferSize: 100000000,
17:39:37 web.1  |         transports: [Object],
17:39:37 web.1  |         allowUpgrades: true,
17:39:37 web.1  |         allowRequest: [Function],
17:39:37 web.1  |         cookie: 'io',
17:39:37 web.1  |         ws: [Object],
17:39:37 web.1  |         _events: [Object] } },
17:39:37 web.1  |   sockets:
17:39:37 web.1  |    [ { nsp: [Circular],
17:39:37 web.1  |        server: [Object],
17:39:37 web.1  |        adapter: [Object],
17:39:37 web.1  |        id: 'RfgXeMgHeP_9SQC5AAAC',
17:39:37 web.1  |        client: [Object],
17:39:37 web.1  |        conn: [Object],
17:39:37 web.1  |        rooms: [Object],
17:39:37 web.1  |        acks: {},
17:39:37 web.1  |        connected: true,
17:39:37 web.1  |        disconnected: false,
17:39:37 web.1  |        handshake: [Object],
17:39:37 web.1  |        _events: [Object] },
17:39:37 web.1  |      { nsp: [Circular],
17:39:37 web.1  |        server: [Object],
17:39:37 web.1  |        adapter: [Object],
17:39:37 web.1  |        id: '7TEjGJjWzxObulClAAAD',
17:39:37 web.1  |        client: [Object],
17:39:37 web.1  |        conn: [Object],
17:39:37 web.1  |        rooms: [Object],
17:39:37 web.1  |        acks: {},
17:39:37 web.1  |        connected: true,
17:39:37 web.1  |        disconnected: false,
17:39:37 web.1  |        handshake: [Object],
17:39:37 web.1  |        _events: [Object] } ],
17:39:37 web.1  |   connected:
17:39:37 web.1  |    { RfgXeMgHeP_9SQC5AAAC:
17:39:37 web.1  |       { nsp: [Circular],
17:39:37 web.1  |         server: [Object],
17:39:37 web.1  |         adapter: [Object],
17:39:37 web.1  |         id: 'RfgXeMgHeP_9SQC5AAAC',
17:39:37 web.1  |         client: [Object],
17:39:37 web.1  |         conn: [Object],
17:39:37 web.1  |         rooms: [Object],
17:39:37 web.1  |         acks: {},
17:39:37 web.1  |         connected: true,
17:39:37 web.1  |         disconnected: false,
17:39:37 web.1  |         handshake: [Object],
17:39:37 web.1  |         _events: [Object] },
17:39:37 web.1  |      '7TEjGJjWzxObulClAAAD':
17:39:37 web.1  |       { nsp: [Circular],
17:39:37 web.1  |         server: [Object],
17:39:37 web.1  |         adapter: [Object],
17:39:37 web.1  |         id: '7TEjGJjWzxObulClAAAD',
17:39:37 web.1  |         client: [Object],
17:39:37 web.1  |         conn: [Object],
17:39:37 web.1  |         rooms: [Object],
17:39:37 web.1  |         acks: {},
17:39:37 web.1  |         connected: true,
17:39:37 web.1  |         disconnected: false,
17:39:37 web.1  |         handshake: [Object],
17:39:37 web.1  |         _events: [Object] } },
17:39:37 web.1  |   fns: [],
17:39:37 web.1  |   ids: 0,
17:39:37 web.1  |   acks: {},
17:39:37 web.1  |   adapter:
17:39:37 web.1  |    { nsp: [Circular],
17:39:37 web.1  |      rooms:
17:39:37 web.1  |       { '5MGPNOdO4th_dOuZAAAA': [],
17:39:37 web.1  |         '64rUhxxp--4Qk1MqAAAB': [],
17:39:37 web.1  |         RfgXeMgHeP_9SQC5AAAC: [Object],
17:39:37 web.1  |         '7TEjGJjWzxObulClAAAD': [Object] },
17:39:37 web.1  |      sids:
17:39:37 web.1  |       { RfgXeMgHeP_9SQC5AAAC: [Object],
17:39:37 web.1  |         '7TEjGJjWzxObulClAAAD': [Object] },
17:39:37 web.1  |      encoder: {} },
17:39:37 web.1  |   _events: { connection: [Function] } }

Meu código é:

`` `` `` `JAVASCRIPT

if (game.scoreTeamTwo> game.scoreTeamOne && game.scoreTeamTwo> game.scoreTeamThree && game.scoreTeamTwo> game.scoreTeamFour) {
jogo.vencedor = 2;
io.sockets.emit ('CTFEnd', jogo);
}

// É só ver se a equipe 1 ganhou o jogo e quando isso acontece, emite CTFEnd e todos os outros dados do jogo
`` `` ``

Comentários muito úteis

Isso provavelmente aconteceu porque você estava tentando enviar um objeto com referências circulares, resultando em chamadas recursivas que excediam o tamanho da pilha.

Todos 32 comentários

Encontrei uma "solução" para o meu problema. Posso enviar todos os dados separados, mas NÃO FUNCIONA com objetos

Também tenho recebido esse erro recentemente, a única diferença é que isso acontece apenas ocasionalmente para mim. Testei a manhã toda e não consegui replicar o problema.

Para mim era que só não conseguia emitir para ninguém, quando o obj era demasiado grande.

@BenBals se o objeto for muito grande, uma solução decente é enviá-lo na forma de uma string, ou seja, execute JSON.stringify() nele

Eu tenho outra solução, mas considerei isso.

: cry: olhando para isso

tem o mesmo problema ao emitir um objeto do tipo:
{success: file, content: file.content} em que file.content é o objeto Buffer e todas as outras propriedades do arquivo são strings.

teve que adicionar o conteúdo do campo diretamente aos objetos porque hasBin () verifica apenas o primeiro nível de um objeto. Mas quando ele tentou enviar o buffer, obteve 'Pilha máxima de chamadas excedida'

Você pode replicar isso emitindo o objeto de soquete (pelo menos de uma maneira) então (abaixo estaria do servidor para -> cliente)

socket.emit('crash', {socket:socket});

Estou curioso para saber se isso tem uma solução real.

Alguma atualização sobre esse problema?
EDIT: Meu problema era que eu estava tentando enviar objetos recursivos.

Curiosamente, estou recebendo o mesmo erro ao tentar passar meus dados do Firebase para o cliente.

Erro

node_modules/socket.io/node_modules/has-binary-data/index.js:46

for (var key in obj) {
                ^
RangeError: Maximum call stack size exceeded

Lado do servidor

var DB = new Firebase('https://1234abcd.firebaseIO.com');
var USERS = DB.child("users");

io.sockets.on('connection', function(socket){

    socket.emit('test', {
        db: USERS
    });

});

Achar uma solução...

Desculpe, mas meu chinês não é tão bom. Você faria a gentileza de traduzir isso para o inglês.

Fiz um Google Translate e sai para "Encontrar a solução ..." ... dificilmente vale a pena responder. Só para constar, a solução é não passar uma quantidade tão grande de dados pelo socket.io ... é um socket que foi projetado para respostas rápidas e curtas. Divida sua resposta ou envie-a via ajax. Cancelando a inscrição neste tópico.

Uma coisa que o código socket.io poderia fazer é apenas iterar de forma assíncrona o objeto e suas propriedades, algo como:

var props = [];
var obj = { a: 1, b:2, c: { d: 3, e: 4 } };

function next_prop(callback){
  if (!props.length){
    setTimeout(callback);
    return;
  }
  var prop = props.shift();
  //do whatever with the prop, call parse_obj on it if it's an object
  setTimeout(next_prop);
}

function parse_obj(obj, callback){
  for (var i in obj){
    props.push(i);
  }
  setTimeout(function(){next_prop(callback);});
}

parse_obj(obj);

Claro, este não é um código real que você deve usar, porque é necessário haver um agrupamento de funções para cada objeto individual no caso de estarem aninhados, caso contrário, você terá conflitos com o objeto que está sendo analisado.

Acabei de receber esse erro também, meu código é como: "io.sockets.emit ('key', data);"
tentei simplificar o objeto "dados" e mantê-lo pequeno, e funcionou.
--E, eu olho para o código src, encontrei este:

image_20150306180547

quando seu objeto "data" tem referência de atributo recursivo, este código falha.
por exemplo, proto > proto > proto ...

1 a equipe principal gostaria de uma versão assíncrona de verificação binária, posso ajudar a obter essa configuração

Posso reproduzir o problema enviando o objeto socket :)

+1 IE11 mostra 'sem espaço de pilha' em _hasBinary.

Este erro apareceu para mim quando eu estava tentando enviar todo o objeto socket de volta para o cliente. Tudo que eu _realmente_ precisava era socket.id então o erro desapareceu quando eu devolvi um item menor - no meu caso, socket.id vez de todo o objeto socket

Isso provavelmente aconteceu porque você estava tentando enviar um objeto com referências circulares, resultando em chamadas recursivas que excediam o tamanho da pilha.

@LordMajestros Isso consertou para mim! Obrigado

@GerbenHofman de nada

Eu enfrentei o mesmo erro no NodeJS v4.4.4 e socket.io v1.4.5.

No meu caso, isso acontece após o evento de desconexão.
E isso só acontece quando eu chamo socket.emit mais de 200.000 vezes.

Se o número da chamada socket.emit for em torno de 100.000, esse erro nunca acontecerá.

O parâmetro que uso com emit é sempre string

Esta informação ajuda você?

Só para adicionar ao @shunjikonishi , eu vi isso acontecer assim que enviei exatamente 100.000 eventos via io.emit. Eu tenho uma meia conectada, então parece que 100.000 é uma tampa rígida.

Parece que este é um limite imposto pelo Node (ou pelo tempo de execução do JavaScript) e Socket.io está mantendo uma referência para algo toda vez que você chama emit, então quando você atinge o 100.000º emitir, ele simplesmente cai.

Testado com Node 5.11.0 e Socketio 1.4.8. Minha versão do Node é diferente (assim como meu sistema operacional etc, realmente não tenho ideia de onde vêm esses 100.000) do que @shunjikonishi, o que pode explicar porque eu

Eu executei exatamente o mesmo código usando ws.js e funcionou bem, ultrapassou 100.000 emissões de soquete sem problema, então talvez seja um problema com Socket.io.

Há alguma chance de o Socket IO implementar uma proteção contra dados recursivos sendo emitidos e falhar com esse erro? Uma detecção simples pode evitar que esse erro aconteça e, em vez disso, emitir um erro mais significativo, como "Você pode estar tentando enviar dados circulares". Isso tornaria mais fácil depurar.

Existem algumas soluções decentes para detectar dados circulares: https://stackoverflow.com/questions/14962018/detecting-and-fixing-circular-references-in-javascript

Uma solução curta depende de JSON.stringify para encontrar o problema:

function isObjectCircular(obj) {
    try {
        JSON.stringify(circularReference);
    } catch(err) {
        return (err.toString() === 'TypeError: Converting circular structure to JSON');
    }
    return false;
}

Mais detalhes: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value

@adamreisnz Você está convidado a fazer um PR;)

Sempre fico surpreso quando alguém me menciona 5 meses depois de fazer um comentário 😆

Eu também encontrei esse erro. E é por isso que perco duas horas para verificar todos os meus códigos: <.
Acho que é causado por objeto de recursão como A = {B: {C: A}} e, obviamente, socket é um objeto de recursão.
Na verdade, acho que aconteceu porque no buffer.js, eles usam uma recursão para encontrar o comprimento dos dados que você deseja enviar. E então obtenha o "tamanho máximo da pilha de chamadas excedido". Se eles usarem JSON.stringify () para obter o comprimento, haverá outro erro claro sobre "estrutura circular". A propósito, tem uma palavra-chave ruim para google!

Eu também corri para isso.

hasBin preencherá a pilha com um loop recursivo se tentar enviar um objeto que tenha uma referência circular.

Droga, vejo que há um PR que está aberto há 2 anos que resolveria isso:

@dustingraham a correção parece ótima, embora eu não tenha certeza sobre as implicações de desempenho.

Além disso, mesmo que ele não lance mais o método hasBinary , temo que o método JSON.stringify que é chamado posteriormente no código ainda gerará um erro:

> var a = {};
undefined
> var b = { a: a };
undefined
> a.b = b
{ a: { b: [Circular] } }
> JSON.stringify(a)
Thrown:
TypeError: Converting circular structure to JSON
    at JSON.stringify (<anonymous>)
Esta página foi útil?
0 / 5 - 0 avaliações