Что происходит с необработанными событиями socket.io?

Socket.io игнорирует \ отбрасывает их? Причина, по которой я спрашиваю об этом, заключается в следующем. Есть клиент с несколькими состояниями. Каждое состояние имеет свой собственный набор обработчиков сокетов. В разные моменты сервер уведомляет клиента об изменении состояния и после этого отправляет несколько сообщений, зависящих от состояния. Но! Клиенту требуется некоторое время, чтобы изменить состояние и установить новые обработчики. В этом случае клиент может пропустить несколько сообщений... потому что на данный момент нет обработчиков. Если я правильно понимаю, необработанные сообщения теряются для клиента.

Может быть, я пропускаю концепцию или делаю что-то не так... Как решить эту проблему?

2 ответа

Необработанные сообщения просто игнорируются. Это так же, как когда происходит событие, и для этого события нет прослушивателей. Сокет получает сообщение и не находит обработчик для него, поэтому с ним ничего не происходит.

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

Ответ jfriend00 хороший, и вы, вероятно, в порядке, просто оставив обработчики на месте и используя логику в обратном вызове, чтобы игнорировать события по мере необходимости. Если вы действительно хотите управлять необработанными пакетами, читайте дальше...

Вы можете получить список обратных вызовов из внутренних компонентов сокета и использовать его для сравнения с заголовком входящего сообщения. Этот код на стороне клиента сделает именно это.

// Save a copy of the onevent function
socket._onevent = socket.onevent;

// Replace the onevent function with a handler that captures all messages
socket.onevent = function (packet) {
  // Compare the list of callbacks to the incoming event name
  if( !Object.keys(socket._callbacks).map(x => x.substr(1)).includes(packet.data[0]) ) {
    console.log(`WARNING: Unhandled Event: ${packet.data}`);

  }
  socket._onevent.apply(socket, Array.prototype.slice.call(arguments));
};

Объект socket._callbacks содержит обратные вызовы, а ключи - это имена. К ним добавлен $, так что вы можете обрезать его по всему списку, сопоставив подстроку (1). Это приводит к хорошему чистому списку названий событий.

ВАЖНОЕ ПРИМЕЧАНИЕ: как правило, вы не должны пытаться внешне изменить какой-либо элемент объекта, начиная с подчеркивания. Также ожидайте, что любые данные в нем нестабильны. Подчеркивание указывает на то, что оно предназначено для внутреннего использования в этом объекте, классе или функции. Хотя этот объект нестабилен, он должен быть достаточно современным, чтобы мы могли его использовать, и мы не модифицируем его напрямую.

Имя события сохраняется в первой записи в файле packet.data. Просто проверьте, есть ли он в списке, и подайте сигнал тревоги, если его нет. Теперь, когда вы отправляете событие с сервера, клиент не знает, что это заметит в консоли браузера.

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

// Save a copy of the onevent function
socket._onevent = socket.onevent;

// Make buffer and configure buffer timings
socket._packetBuffer = [];
socket._packetBufferWaitTime = 1000; // in milliseconds
socket._packetBufferPopDelay = 50;   // in milliseconds

function isPacketUnhandled(packet) {
  return !Object.keys(socket._callbacks).map(x => x.substr(1)).includes(packet.data[0]);
}

// Define the function that will process the buffer
socket._packetBufferHandler = function(packet) {
  if( isPacketUnhandled(packet) ) {
    // packet can't be processed yet, restart wait cycle
    socket._packetBuffer.push(packet);
    console.log(`packet handling not completed, retrying`)
    setTimeout(socket._packetBufferHandler, socket._packetBufferWaitTime, socket._packetBuffer.pop());
  }
  else {
    // packet can be processed now, start going through buffer
    socket._onevent.apply(socket, Array.prototype.slice.call(arguments));
    if(socket._packetBuffer.length > 0) {
      setTimeout(socket._packetBufferHandler,socket._packetBufferPopDelay(), socket._packetBuffer.pop());
    }
    else {
      console.log(`all packets in buffer processed`)
      socket._packetsWaiting = false;
    }
  }
}

// Replace the onevent function with a handler that captures all messages
socket.onevent = function (packet) {
  // Compare the list of callbacks to the incoming event name
  if( isPacketUnhandled(packet) ) {
    console.log(`WARNING: Unhandled Event: ${packet.data}`);
    socket._packetBuffer.push(packet);
    if(!socket._packetsWaiting) {
      socket._packetsWaiting = true;
      setTimeout(socket._packetBufferHandler, socket._packetBufferWaitTime, socket._packetBuffer.pop());
    }
  }
  socket._onevent.apply(socket, Array.prototype.slice.call(arguments));
};

Здесь необработанные пакеты помещаются в буфер и запускается таймер. По истечении заданного количества времени if начинает проверку, чтобы увидеть, готовы ли обработчики для каждого элемента. Каждый обрабатывается до тех пор, пока все не будут исчерпаны или отсутствует обработчик, который вызывает другое ожидание.

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

Я протестировал его с очень длинными строками, и он смог их протолкнуть, поэтому то, что они называют "пакетом", вероятно, не является стандартным пакетом.

Протестировано с SocketIO версии 2.2.0 на Chrome.

Я не уверен насчет socket.io, но JavaScript является однопоточным. Если event A срабатывает и есть слушатель для этого события, который блокирует цикл события. Второе событие (давайте назовем это event B) не может быть запущен, пока этот слушатель не закончит работу. Так что, если вы устанавливаете обработчики событий непосредственно внутри слушателя для event AНет расы.

Другие вопросы по тегам