Какая проверка гарантирует, что все записи были прочитаны из одного сообщения, и пришло время удалить это событие и поставить в очередь событие ответа, которое запишет ответ обратно клиенту?
Я бы надеялся, что у вашего сообщения есть заголовок с количеством записей в нем. В противном случае протокол, который у вас есть, вероятно, не поддается анализу.
Что вам нужно, так это иметь анализатор с отслеживанием состояния, который потребляет все доступные байты и выводит записи после их завершения. Такой синтаксический анализатор должен приостановить свое состояние, как только он достигнет последнего байта расшифрованного ввода, а затем должен быть вызван снова, когда станет доступно больше данных для чтения. Но во всех случаях, если вы не можете заранее предсказать, сколько данных ожидается, вы не сможете определить, когда сообщение будет завершено - это если вы не используете протокол самосинхронизации. Что-то вроде заголовков банкоматов могло бы стать отправной точкой. Но в таком усложнении нет необходимости, когда все, что вам нужно, это просто правильно разграничить ваши данные, чтобы парсер пакетов мог точно знать, есть ли у него все необходимое или нет.
Это проблема с отправкой сообщений: это очень легко отправлять материал, который не может быть декодирован получателем, поскольку отправитель прекрасно переносит потерю данных - ему просто все равно. Но получателю обязательно нужно будет каким-то образом знать, сколько байтов или записей ожидается. Об этом можно сообщить априори, отправив заголовки, которые включают счетчики байтов или счетчики записей фиксированного размера (это одна и та же информация о размере только в разных единицах), или апостериори, используя уникальные разделители записей. Например, при отправке печатаемого текста, разбитого на строки, такими разделителями могут быть разделители абзацев Unicode (U + 2029).
Очень важно убедиться, что разделители записей не могут встречаться в самих данных записи. Таким образом, вам нужен какой-то механизм «наполнения», при котором, если последовательность разделителей появляется в полезной нагрузке, вы можете изменить ее так, чтобы она больше не была допустимым разделителем. Вам также нужен механизм «распаковки», чтобы такие измененные последовательности разделителей можно было обнаружить и преобразовать обратно в их исходную форму, конечно, без интерпретации как разделитель. Очень простой пример такого процесса разделения - кадрирование с заполнением октетами в протоколе PPP . Это форма обрамления HDL C. Разделитель записей - 0x7E
. Каждый раз, когда этот байт обнаруживается в полезной нагрузке, он экранируется - заменяется последовательностью 0x7D 0x5E
. На принимающей стороне 0x7D
интерпретируется как означающее «следующий символ был подвергнут XOR с 0x20». Таким образом, получатель сначала преобразует 0x7D 0x5E
в 0x5E
(удаляет escape-байт), а затем выполняет XOR с 0x20
, давая исходный 0x7E
. Такое кадрирование легко реализовать, но потенциально оно связано с большими накладными расходами, чем кадрирование с более длинной последовательностью разделителей или даже последовательностью разделителей динамического c, форма которой отличается для каждой позиции в потоке. Это можно использовать для предотвращения атак типа «отказ в обслуживании», когда злоумышленник может злонамеренно предоставить полезную нагрузку, которая повлечет за собой большие накладные расходы на побег. Последовательность разделителей динамических c - особенно если она непредсказуема, например, путем согласования новой последовательности для каждого соединения - предотвращает такое ухудшение качества обслуживания.