Объектно-ориентированная сеть
Я написал несколько сетевых систем и имею хорошее представление о том, как работает сеть. Однако у меня всегда есть функция приема пакетов, которая является гигантским оператором коммутатора. Это начинает действовать мне на нервы. Я бы предпочел хороший элегантный объектно-ориентированный способ обработки пакетов, но каждый раз, когда я пытаюсь придумать хорошее решение, я всегда заканчиваю тем, что не хватает.
например, скажем, у вас есть сетевой сервер. Это просто ожидание. ответы. Приходит пакет, и сервер должен проверить пакет, а затем он должен решить, как его обрабатывать.
на данный момент я делаю это, включив идентификатор пакета в заголовке, а затем имея огромную кучу вызовов функций, которые обрабатывают каждый тип пакета. Со сложными сетевыми системами это приводит к монолитному заявлению коммутатора, и мне действительно не нравится обращаться с ним таким образом. Один из способов, который я рассмотрел, - использовать карту классов обработчиков. Тогда я смогу. передайте пакет соответствующему классу и обработайте входящие данные. Проблема в том, что мне нужно каким-то образом "зарегистрировать" каждый обработчик пакетов с картой. Это означает, что, как правило, мне нужно создать статическую копию класса, а затем в конструкторе зарегистрировать его с помощью центрального обработчика пакетов. Хотя это работает, это действительно кажется неэлегантным и неудобным способом его обработки.
редактировать: в равной степени было бы идеально иметь хорошую систему, которая работает как пути. т. е. структура класса, которая легко обрабатывает отправку тех же типов пакетов, что и их получение (очевидно, через разные функции).
может ли кто-нибудь указать мне на лучший способ обработки входящих пакетов? Ссылки и полезная информация очень ценятся!
извинения, если я не описал свою проблему хорошо, а моя неспособность описать ее хорошо также является причиной того, что мне никогда не удавалось придумать решение.
6 ответов
о способе обработки типа пакета: для меня карта является лучшей. Однако я бы использовал простой массив (или вектор) вместо карты. Это сделало бы время доступа постоянным, если вы перечисляете типы пакетов последовательно от 0.
Что касается структуры классов. Есть библиотеки, которые уже выполняют эту работу:доступные языки определения сетевого протокола игры и генерация кода. Е. Г. буфер протокола Google кажется многообещающим. Он создает класс хранения с геттерами, сеттерами, процедурами сериализации и десериализации для каждого сообщения в описании протокола. Язык описания протокола выглядит более или менее богатым.
карта экземпляров обработчика-это в значительной степени лучший способ справиться с этим. В этом нет ничего неэлегантного.
по моему опыту, анализ с использованием таблиц является наиболее эффективным методом.
хотя std::map
приятно, я в конечном итоге использую статические таблицы. The std::map
невозможно статически инициализировать как постоянную таблицу. Он должен быть загружен во время выполнения. Таблицы (массивы структур) могут быть объявлены как данные и инициализированы во время компиляции. Я не встречал достаточно больших таблиц, где линейный поиск был узким местом. Обычно размер таблицы достаточно мал, чтобы накладные расходы в двоичном файле поиск медленнее, чем линейный поиск.
для высокой производительности, я буду использовать данные сообщения в качестве индекса в таблице.
когда вы делаете ООП, вы пытаетесь представить каждую вещь как объект, верно? Таким образом, ваши сообщения протокола также становятся объектами; у вас, вероятно, будет базовый класс YourProtocolMessageBase
который инкапсулирует поведение любого сообщения и от которого вы унаследуете свои полиморфно специализированные сообщения. Тогда вам просто нужен способ превратить каждое сообщение (т. е. каждый YourProtocolMessageBase
экземпляр) в строку байтов, и наоборот. Такие методы называются сериализация методы; некоторые на основе метапрограммирования реализации.
быстрый пример в 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 идентификаторы ⇔. Это выглядит действительно неизбежным.
вы хотите продолжать использовать тот же сетевой протокол пакета, но перевести это в объект в программировании, верно ?
существует несколько протоколов, которые позволяют обрабатывать данные как объекты программирования, но, похоже, вы не хотите изменять протокол, как он обрабатывается в вашем приложении.
пакеты поставляются с чем-то вроде "тега" или метаданных или любого "id" или "типа данных", который позволяет сопоставлять с определенным классом объектов ? Если да, то ты может создать массив, в котором хранится идентификатор. и соответствующий класс и создать объект.
более OO способ справиться с этим заключается в создании государственной машины с использованием шаблона состояния.
обработка входящих необработанных данных анализирует, где государственные машины обеспечивают элегантное решение (вам придется выбирать между элегантным и производительностью)
У вас есть буфер данных для обработки, каждое состояние имеет метод буфера дескриптора, который анализирует и обрабатывает его часть буфера (если это уже возможно) и устанавливает следующее состояние на основе содержимого.
Если вы хотите пойти для производительности, вы все еще можете использовать государственную машину, но оставить ОО части.