разбор протокола в c - PullRequest
10 голосов
/ 04 июня 2010

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

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

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

Так что, хотя этот протокол довольно прост, мой код представляет собой целую кучу операторов switch / case, и я много читаю из файла / сокета, что я считаю не оптимальным. Моя главная цель - сделать этот декодер максимально быстрым. Для более опытных людей это путь или есть лучший путь, который я еще не выяснил? Любое изящное решение этой проблемы?

Ответы [ 5 ]

11 голосов
/ 04 июня 2010

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

  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
}

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

3 голосов
/ 04 июня 2010

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

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

2 голосов
/ 04 июня 2010

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

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

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

1 голос
/ 04 июня 2010

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

1 голос
/ 04 июня 2010

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

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

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

...