Socket.io: RangeError : taille maximale de la pile d'appels dépassée

Créé le 4 juil. 2014  ·  32Commentaires  ·  Source: socketio/socket.io

J'appelle juste `ìo.sockets.emit('hey', data); and it will crash with RangeError : la taille maximale de la pile d'appels a dépassé . 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``.

Je ne sais pas où est le problème. J'ai essayé d'enregistrer io.sockets juste avant de l'utiliser et il affiche ceci :

{ 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] } }

Mon code est :

`````` JAVASCRIPT

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

//Il cherche juste si l'équipe 1 a gagné le jeu et quand c'est le cas, elle émet CTFEnd et toutes les autres données du jeu
``````

Commentaire le plus utile

Cela s'est probablement produit parce que vous essayiez d'envoyer un objet avec des références circulaires entraînant des appels récursifs dépassant la taille de la pile.

Tous les 32 commentaires

J'ai trouvé une "solution" à mon problème. Je peux envoyer toutes les données séparées, mais cela ne fonctionne PAS avec les objets

J'ai également eu récemment cette erreur, la seule différence étant que cela ne m'arrive qu'occasionnellement. J'ai testé toute la matinée et je n'ai pas réussi à reproduire le problème.

Pour moi, c'était seulement qu'il n'était pas capable d'émettre à tout le monde, quand l'obj était trop grand.

@BenBals si l'objet est trop gros, une solution de contournement décente consiste à l'envoyer sous forme de chaîne, c'est-à-dire à exécuter JSON.stringify() dessus

J'ai eu un autre travail, mais j'y ai pensé.

:cry: regarde ça

a le même problème lors de l'émission d'un objet de type :
{success : file, content : file.content} où file.content est l'objet Buffer et toutes les autres propriétés du fichier sont des chaînes.

a dû ajouter le contenu du champ directement aux objets car hasBin() ne vérifie que le premier niveau d'un objet. Mais lorsqu'il a ensuite essayé d'envoyer le tampon, il a obtenu un « dépassement de la pile d'appels maximale »

Vous pouvez répliquer cela en émettant l'objet socket (au moins dans un sens) donc (Ci-dessous serait le serveur vers -> client )

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

Je suis curieux de savoir si cela a un correctif réel cependant.

Une mise à jour sur ce problème ?
EDIT : Mon problème était que j'essayais d'envoyer des objets récursifs.

Chose intéressante, j'obtiens la même erreur lorsque j'essaie de transmettre mes données Firebase au côté client.

Erreur

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

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

Du côté serveur

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

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

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

});

Trouver une solution...

Désolé, mais mon chinois n'est pas très bon. Auriez-vous la gentillesse de traduire ceci en anglais.

Est-ce qu'une traduction de Google et il en ressort "Trouvez la solution..." ... à peine la peine de répondre. Pour mémoire, la solution est de ne pas transmettre une telle quantité de données via socket.io ... c'est un socket qui a été conçu pour des réponses rapides et courtes. Séparez votre réponse ou envoyez-la via ajax. Désabonnement à ce fil.

Une chose que le code socket.io pourrait faire est simplement d'itérer de manière asynchrone l'objet et ses propriétés, quelque chose comme :

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

Bien sûr, ce n'est pas du code réel que vous devriez utiliser, car il doit y avoir un emballage de fonctions pour chaque objet individuel au cas où ils seraient imbriqués, sinon vous aurez des conflits avec l'objet en cours d'analyse.

J'ai juste cette erreur aussi, mon code est comme : "io.sockets.emit('key', data);"
j'essaie de simplifier l'objet "données" et de le garder petit, et cela a fonctionné.
--ET, je regarde dans le code src, j'ai trouvé ceci :

image_20150306180547

lorsque votre objet "data" a un attribut récursif faisant référence, ce code planterait.
par exemple proto > proto > proto ...

+1 l'équipe principale aimerait-elle une version asynchrone de la vérification binaire, je peux aider à obtenir cette configuration

Je peux reproduire le problème en envoyant l'objet socket :)

+1 IE11 affiche « l'espace de la pile » dans _hasBinary.

Cette erreur s'est produite pour moi lorsque j'essayais de renvoyer l'ensemble de l'objet socket au client. Tout ce dont j'avais _vraiment_ besoin était de socket.id donc l'erreur a disparu lorsque j'ai renvoyé un article plus petit - dans mon cas, socket.id au lieu de l'objet entier socket

Cela s'est probablement produit parce que vous essayiez d'envoyer un objet avec des références circulaires entraînant des appels récursifs dépassant la taille de la pile.

@LordMajestros Cela l'a corrigé pour moi ! Merci

@GerbenHofman de rien

J'ai rencontré la même erreur sur NodeJS v4.4.4 et socket.io v1.4.5.

Dans mon cas, cela se produit après l'événement de déconnexion.
Et cela n'arrive que lorsque j'ai appelé socket.emit plus de 200 000 fois.

Si le nombre d'appels socket.emit est d'environ 100 000, cette erreur ne se produit jamais.

Le paramètre que j'utilise avec emit est toujours une chaîne

Ces informations vous aident-elles ?

Juste pour ajouter à @shunjikonishi , j'ai vu cela se produire dès que j'ai envoyé exactement 100 000 événements via io.emit. J'ai 1 chaussette connectée, il semble donc que 100 000 soit un plafond dur.

On dirait que c'est une limite imposée par Node (ou l'environnement d'exécution JavaScript) et Socket.io contient une référence à quelque chose à chaque fois que vous appelez l'émission, donc lorsque vous atteignez la 100 000e émission, elle tombe tout simplement.

Testé avec Node 5.11.0 et Socketio 1.4.8. Ma version de Node est différente (ainsi que mon système d'exploitation, etc., vraiment aucune idée d'où viennent ces 100 000) que @shunjikonishi, ce qui peut expliquer pourquoi j'ai atteint la limite à 100 000 alors qu'il est passé à 200 000.

J'ai exécuté exactement le même code en utilisant ws.js et cela a bien fonctionné, j'ai dépassé les 100 000 sockets émis sans problème, alors c'est peut-être un problème avec Socket.io.

Existe-t-il une chance que Socket IO implémente une protection contre l'émission de données récursives et l'échec avec cette erreur ? Une simple détection pourrait empêcher cette erreur de se produire et émettre à la place une erreur plus significative, comme "Vous essayez peut-être d'envoyer des données circulaires". Cela faciliterait le débogage.

Il existe de nombreuses solutions décentes pour détecter les données circulaires : https://stackoverflow.com/questions/14962018/detecting-and-fixing-circular-references-in-javascript

Une solution courte repose sur JSON.stringify pour trouver le problème :

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

Plus de détails : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value

@adamreisnz Vous êtes invités à faire un PR ;)

Ça me prend toujours au dépourvu quand quelqu'un me mentionne 5 mois après que j'ai fait un commentaire 😆

J'ai rencontré cette erreur aussi. Et c'est pourquoi je perds deux heures à vérifier tous mes codes :<.
Je pense que cela est dû à un objet de récursivité comme A={B:{C:A}}, et évidemment socket est un objet de récursivité.
En fait, je suppose que cela s'est produit car dans buffer.js, ils utilisent une récursivité pour trouver la longueur des données que vous souhaitez envoyer. Et puis obtenez la "taille maximale de la pile d'appels dépassée". S'ils utilisent JSON.stringify() pour obtenir la longueur, il y aura clairement une autre erreur sur la "structure circulaire". D'ailleurs, il a un mauvais mot clé sur google !

Je viens de tomber sur ça aussi.

hasBin remplira la pile avec une boucle récursive si vous essayez d'envoyer un objet qui a une référence circulaire.

Shucks, je vois qu'il y a un PR qui est ouvert depuis 2 ans qui résoudrait ce problème :

@dustingraham le correctif a l'air génial, bien que je ne sois pas sûr des implications sur les performances.

De plus, même s'il ne lance plus la méthode hasBinary , je crains que la méthode JSON.stringify qui est appelée plus tard dans le code ne génère toujours une erreur :

> 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>)
Cette page vous a été utile?
0 / 5 - 0 notes