Объектно-ориентированная организация

Я написал несколько сетевых систем и хорошо знаю, как работает сеть. Однако я всегда получаю функцию приема пакетов, которая является гигантским оператором switch. Это начинает добираться до меня. Я бы предпочел неплохой элегантный объектно-ориентированный способ обработки принимающих пакетов, но каждый раз, когда я пытаюсь найти хорошее решение, я всегда заканчиваю короткое замыкание.

Например, скажем, у вас есть сетевой сервер. Он просто ждет ответа. Пакет входит, и серверу необходимо проверить пакет, а затем ему нужно решить, как его обрабатывать.

В данный момент я делаю это, включив идентификатор пакета в заголовке и затем имея огромную совокупность вызовов функций, которые обрабатывают каждый тип пакета. Со сложными сетевыми системами это приводит к монолитной инструкции switch, и мне действительно не нравится обрабатывать ее таким образом. Один из способов, который я рассмотрел, - использовать карту классов обработчиков. Затем я могу передать пакет соответствующему классу и обработать входящие данные. Проблема с этим заключается в том, что мне нужно каким-то образом "зарегистрировать" каждый обработчик пакетов с помощью карты. Это означает, что, как правило, мне нужно создать статическую копию класса, а затем в конструкторе зарегистрировать его с помощью центрального обработчика пакетов. В то время как это работает, это действительно похоже на неэлегантный и неудобный способ обработки.

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

Может ли кто-нибудь указать мне на лучший способ обработки входящих пакетов? Ссылки и полезная информация очень ценятся!

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

Ответы

Ответ 1

О способе обработки типа пакета: для меня карта лучшая. Однако я бы использовал простой массив (или вектор) вместо карты. Это сделает постоянным время доступа, если вы последовательно перечисляете свои типы пакетов с 0.

Что касается структуры класса. Есть библиотеки, которые уже выполняют эту работу: Доступные языки определения сетевых протоколов игры и генерация кода. Например. Протокол протокола Google кажется многообещающим. Он генерирует класс хранения с геттерами, сеттерами, процедурами сериализации и десериализации для каждого сообщения в описании протокола. Язык описания протокола выглядит более или менее богатым.

Ответ 2

Карта экземпляров обработчика - это самый лучший способ справиться с этим. Ничего необычного в этом.

Ответ 3

По моему опыту, синтаксический анализ, основанный на таблицах, является наиболее эффективным методом.

Хотя std::map хорошо, я в конечном итоге использую статические таблицы. std::map не может быть статически инициализирован как таблица констант. Он должен быть загружен во время выполнения. Таблицы (массивы структур) могут быть объявлены как данные и инициализированы во время компиляции. Я не сталкивался с таблицами, достаточно большими, где линейный поиск был узким местом. Обычно размер таблицы достаточно мал, что накладные расходы в двоичном поиске медленнее, чем линейный поиск.

Для высокой производительности я буду использовать данные сообщения как индекс в таблице.

Ответ 4

Когда вы делаете ООП, вы пытаетесь представить каждую вещь как объект, не так ли? Таким образом, ваши сообщения протокола тоже становятся объектами; вы, вероятно, будете иметь базовый класс YourProtocolMessageBase, который будет инкапсулировать любое поведение сообщения и из которого вы наследуете ваши полиморфно специализированные сообщения. Тогда вам просто нужен способ превратить каждое сообщение (т.е. Каждый экземпляр YourProtocolMessageBase) в строку байтов и способ сделать обратное. Такие методы называются serialization; существуют некоторые метапрограммируемые варианты.

Быстрый пример в Python:

from socket import *
sock = socket(AF_INET6, SOCK_STREAM)
sock.bind(("localhost", 1234))
rsock, addr = sock.accept()

Сервер блокирует запуск другого экземпляра для клиента:

from socket import *
clientsock = socket(AF_INET6, SOCK_STREAM)
clientsock.connect(("localhost", 1234))

Теперь используйте встроенный модуль сериализации Python, pickle; Клиент:

import pickle
obj = {1: "test", 2: 138, 3: ("foo", "bar")}
clientsock.send(pickle.dumps(obj))

Сервер:

>>> import pickle
>>> r = pickle.loads(rsock.recv(1000))
>>> r
{1: 'test', 2: 138, 3: ('foo', 'bar')}

Итак, как вы можете видеть, я просто отправил ссылку-local на объект Python. Разве это не ООП?

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

Ответ 5

Вы хотите продолжать использовать один и тот же протокол пакетной сети, но перевести это в объект в программировании, правильно?

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

Имеются ли пакеты с чем-то вроде "тега" или метаданных или любого "идентификатора" или "типа данных", который позволяет вам сопоставлять определенный класс объектов? Если это так, вы можете создать массив, в котором хранится идентификатор. и соответствующий класс, и сгенерировать объект.

Ответ 6

Более OO способ справиться с этим заключается в создании конечного автомата с использованием шаблона состояния.

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

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

Если вы хотите перейти на производительность, вы все равно можете использовать конечный автомат, но оставьте часть OO.