парсинг протокола в c

Я играл вокруг с попыткой реализовать некоторые декодеры протокола, но каждый раз я сталкиваюсь с "простой" проблемой, и я чувствую способ, которым я решаю проблему, не оптимально и должен быть лучший способ сделать вещи. Я использую C. В настоящее время я использую некоторые консервированные данные и читаю их в как файл, но позже это было бы через TCP или UDP.

Вот проблема. Я в настоящее время играю с протоколом двоичной синхронной передачи данных на работе. Все поля 8 битов длиной. Первое поле (8bits) является типом пакета. Таким образом, я читал в первых 8 битах и использование переключателя/случая, который я вызываю функцию для чтения в остальной части пакета, поскольку я затем знаю размер/структуру его. НО... некоторые из этих пакетов вложили пакеты в них, поэтому когда я встречаюсь с тем определенным пакетом, я затем должен читать, еще 8-16 байтов имеют другой переключатель/случай для наблюдения то, что следующий тип пакета и вперед и вперед. (К счастью пакеты только вкладываются 2 или 3 глубоких). Только, после того как мне декодировали целый пакет, может я обрабатывать его к моему конечному автомату для обработки.

Я предполагаю, что это может быть более общим вопросом также. Сколько данных необходимо считать за один раз с сокета? Как можно больше? Так же как что "подобно" в заголовках протокола?

Таким образом даже при том, что этот протокол является довольно базовым, мой код является целым набором переключателя/операторов выбора, и я делаю большое чтение из файла/сокета, который я чувствую, не оптимально. Моя основная цель состоит в том, чтобы сделать этот декодер максимально быстро. Более опытным людям там, действительно ли это - способ пойти или существует ли лучший способ, которым я просто еще не выяснил? Какое-либо изящное решение этой проблемы?

10
задан NomadAlien 7 June 2010 в 13:36
поделиться

5 ответов

Я рекомендую этот подход:

  1. Прочтите все, что вы можете, из файла / сокета (отделите передачу данных от фактического протокола)
  2. Передайте данные, которые вы прочитали, в процедуру обработки данных

Псевдо Код на C (представьте, что destinationBuffer представляет собой кольцевой буфер - я считаю, что такая структура данных жизненно важна в случае приложений, которым необходимо анализировать много входящих данных):

forever()
{
  // this function adds data to the buffer updating it
  read_all_you_can(destinationBuffer);
  ...
  handle_data(destinationBuffer);
  // the buffer is automatically adjusted in order
  // to reflect how much of the data was processed
}

Обычно лучше читать столько же насколько это возможно, чтобы иметь большую производительность.

11
ответ дан 3 December 2019 в 20:40
поделиться

Сколько данных вы должны читать из сокета за раз?

Это TCP или UDP: ориентировано ли оно на потоки или на пакеты? Есть ли у вас способ узнать длину сообщения, прежде чем вы узнаете его тип? Если нет, не могли бы вы (например, изменив протокол, чтобы убедиться, что первое поле содержит / определяет длину сообщения?

Чтобы сделать то, что вы делаете, вам нужно несколько раз прочитать из сокета. Это может быть быстрее / проще, если бы вы могли прочитать все сообщение в своей памяти / ОЗУ, а затем расшифровать его там.

1
ответ дан 3 December 2019 в 20:40
поделиться

Помните, что read() абсолютно свободно временно игнорирует размер, который вы ему задали, и пытается читать в выровненных по аркам границах (8/16/32/64). read() может делать это до тех пор, пока возвращает вам именно то количество байт (или меньше), которое вы запросили. Таким образом, за кулисами уже может происходить довольно много оптимизации.

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

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

2
ответ дан 3 December 2019 в 20:40
поделиться

PADS призван помочь вам решить именно такую проблему. Он сгенерирует для вас очень эффективный синтаксический анализатор на языке C. Вы пишете декларативное описание формата пакета, а PADS делает это дальше.

1
ответ дан 3 December 2019 в 20:40
поделиться

Не поддавайтесь соблазну преждевременной оптимизации. Сначала сделайте так, чтобы все работало, и только потом думайте о том, нужна ли оптимизация. Если это так, делайте это с научной точки зрения: оцените код с помощью бенчмарка и сначала попробуйте самые низко висящие плоды, не полагайтесь на интуицию.

Не забывайте, что ваша ОС, скорее всего, будет сама буферизировать данные, независимо от того, читаете ли вы из файла или сокета. Тем не менее, повторяющиеся системные вызовы, скорее всего, будут узким местом, так что они могут стать прямой победой в оптимизации. На прежнем месте работы мы избегали этой проблемы, явно указывая в заголовке пакета его длину (никогда не более 8k): таким образом мы точно знали, сколько нужно считать в массив, а затем наш собственный код буферизации брал это на себя.

3
ответ дан 3 December 2019 в 20:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: