JS: Максимальный размер стека вызовов превышен при разъединении, несмотря на то, что функция пуста - PullRequest
0 голосов
/ 14 февраля 2019

Я в некотором роде новичок в js и экспериментирую с модулем net.

У меня работает простой сервер, который распределяет пакеты по всем известным клиентам, но как только один клиент отключается, сервер «падает» на конце'событие, несмотря на то, что эта функция включает только команду log:

var clients = [];

const server = net.createServer((c) => {

  // 'connection' listener
  console.log("client connected")
  clients.push(c);

  c.on('end', () => {
    console.log('client disconnected');
    //dc(c)
  });

  c.on('error', () => {
    c.write("400");
  })

  c.on('data', (data) => {

    console.log(data.toString())
    writeAll(c, data);

  });

});

Функция Write all - это функция ниже, которая распределяет входящий пакет всем известным клиентам, за исключением c:

function writeAll(exclude, buffer) {

  clients.forEach((client) => {
    if(client != exclude) {
      client.write(buffer);
    }
  });

}

dc () - это функция, которая должна удалять клиента из списка массивов, который я определил ранее, но закомментирован, поэтому это не должно иметь значения.Все работает нормально, пока один клиент не отключится, то есть, когда я получу следующее сообщение об ошибке:

internal/errors.js:207
function getMessage(key, args) {
                   ^

RangeError: Maximum call stack size exceeded
    at getMessage (internal/errors.js:207:20)
    at new NodeError (internal/errors.js:153:13)
    at doWrite (_stream_writable.js:411:19)
    at writeOrBuffer (_stream_writable.js:399:5)
    at Socket.Writable.write (_stream_writable.js:299:11)
    at Socket.c.on (C:\Users\...\server.js:17:7)
    at Socket.emit (events.js:197:13)
    at errorOrDestroy (internal/streams/destroy.js:98:12)
    at onwriteError (_stream_writable.js:430:5)
    at onwrite (_stream_writable.js:461:5)

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

Я был бы очень признателен, если бы один из вас, профессионалов, принял это на себя, так как я полностьюпотерян.

Редактировать: Полный файл можно найти здесь: https://ghostbin.com/paste/knm3f

Редактировать 2: Я переустановил сетевой модуль, и теперь ошибка изменилась, оставив меня еще более запутанным:

internal/errors.js:222
  const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length;
                              ^
RangeError: Maximum call stack size exceeded
    at String.match (<anonymous>)
    at getMessage (internal/errors.js:222:31)
    at new NodeError (internal/errors.js:153:13)
    at doWrite (_stream_writable.js:411:19)
    at writeOrBuffer (_stream_writable.js:399:5)
    at Socket.Writable.write (_stream_writable.js:299:11)
    at Socket.c.on (C:\Users\...\server.js:17:7)
    at Socket.emit (events.js:197:13)
    at errorOrDestroy (internal/streams/destroy.js:98:12)
    at onwriteError (_stream_writable.js:430:5)

Редактировать 3: Код, который определенно работал, был протестирован и по какой-то причине сталкивается с той же проблемой даже на машине, на которой он был написан, а также на моем ноутбуке.У меня такое ощущение, что это как-то не по мне.

Редактировать 4: SOOOOOOOOOOO ... Оказывается, Microsoft снова ударила кулаком по моему заднему концу.Он отлично работает на дистрибутиве Linux.Я бы сделал ставку на изменение брандмауэра Windows или другое обновление, с которым я не согласился.Спасибо Microsoft.Вы заставляете меня перейти на Linux.

Редактировать 5: Ошибка при отключении возникла только в Windows, но переполнение стека было вызвано записью события ошибки клиенту, который иногда выдавал ошибку, так какклиент больше не был подключен.

1 Ответ

0 голосов
/ 15 февраля 2019

В вашем коде есть две ошибки, и они определенно не слишком очевидны.

Всякий раз, когда вы пишете клиенту, вы делаете это с помощью этого цикла в writeAll():

clients.forEach((client) => {
  if(client != exclude) {
    client.write(buffer);
  }
});

Здесь мы видим, что вы делаете client.write(...).Эта команда может обнаружить, что этот конкретный client закрыл свое соединение.Это означает, что emit('error') произойдет, и ваш соответствующий обратный вызов будет вызван.

В этом обратном вызове вы вызываете dc(c), который, как ожидается, удалит клиента из массива:

clients.splice(index, 1)

Другими словами, вы изменяете массив с именем clients, пока выполняете clients.forEach().Это большой нет нет!Я вообще-то пытаюсь забыть об этой функции forEach().Это создало так много проблем с моей стороны!Некоторые люди говорят, что forEach() также медленнее, чем for() или while() петли.Но эта проблема, возможно, была улучшена.

По крайней мере, вы хотите использовать цикл for():

for(let i = 0; i < clients.length; ++i)
{
   ...
}

Обратите внимание, что я сначала не сохраняю clients.length в переменной, поскольку она может меняться со временем!

Проблема этого подхода заключается в том, что всякий раз, когда возникает splice(), вы пропускаете некоторых клиентов.

Следующая лучшая вещьсделать это использовать цикл, идущий назад.Для этого while() хорошо приспособлен:

let i = clients.length
while(i > 0)
{
    --i
    ...
}

Этот цикл будет работать лучше, если: (1) только один элемент получает splice() d, (2) ни один новый клиент не добавляется всписок.

Последнее, сильное решение, которое я бы предложил, - это сначала скопировать массив:

const client_list = clients.slice()
client_list.forEach((client) => { ... })    // if you really want to use the forEach()?!

Копия не изменится: она локальная и const.

Но почему в стеке испортилось из-за этого?Возможно, потому что в результате ошибки вы делаете write() для клиента ... Хорошо, это не связано с forEach(), но этот код повторяется:

c.on('error', () => {
    c.write("400")     // are you missing a "\n", btw?
});

Как вы можете видеть,c.write() является виновником генерируемой ошибки, в этом обратном вызове on() вы собираетесь зацикливаться вечно ... Вам нужно проверить, является ли c допустимым или перехватить ошибки:

c.on('error', () => {
    try
    {
        c.write("400")     // are you missing a "\n", btw?
    }
    catch(err)
    {
        // maybe log the error?
        console.log(err)
    }
});

При этом я знаю по своему опыту, что после того, как я удалил все вызовы forEach() из своего кода и сделал копии массивов (объектов), я не могу быть уверен, что они не будут изменены под моими ногами, мой кодработал намного лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...