Каковы лучшие практики разработки и реализации сетевых протоколов? - PullRequest
3 голосов
/ 20 октября 2010

В первый раз я пытаюсь реализовать сетевой протокол через TCP / IP.Я разработал один, но я не уверен, что он эффективен.

Дизайн

Итак, вот моя идея: после открытия клиентом соединения TCP / IP с сервером,каждый раз, когда он хочет сделать запрос, он сначала отправляет размер запроса, за которым следует какой-то символ-разделитель (новая строка или пробел) и после этого фактического запроса (тот же принципал используется в HTTP, и я думаю, что в большинстве случаев эта идея используется).

Например, если клиент хочет отправить GET ASD, он фактически отправит 7 GET ASD (при условии, что пространство является разделителем).

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

Реализация

Все это касалось разработки протокола, теперь некоторые замечания оРеальная реализация: я думаю, что основная проблема здесь заключается в эффективной реализации и управлении буферами.

Я думаю, что буфера размером 2 * MAX_SIZE_OF_ONE_REQUEST будет достаточно для обслуживания одного клиента, поскольку чанки, полученные сервером, могут одновременно содержать конец первого запросаи начало второго.Это мое предположение, если я ошибаюсь и нам нужно больше или меньше места, пожалуйста, дайте мне знать, почему.

Я думаю, что есть два способа хранения запросов в буфере, пока они не будут обработаны:

  1. Всякий раз, когда сервер получает новый фрагмент символов, сервер добавляет его в правую часть буфера.Как только буфер будет содержать полный запрос, сервер обработает его и переместит влево все остальное в начале буферного пространства.

  2. Некоторый циклический буфер, который не перемещаетсябуфер в начале после обработки запроса.

Это мои мысли о реализации буферов с учетом асинхронного ввода-вывода (сервер будет использовать epoll / kqueue / select для получения запросов от клиентов).Я думаю, что если сервер не будет использовать асинхронный ввод-вывод для связи с клиентами, то реализация буфера будет намного проще.

Также я не решил, как должен вести себя сервер, когда он получает некорректный запрос.Должно ли это закрыть связь с клиентом?

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

Ответы [ 2 ]

3 голосов
/ 21 октября 2010

Прежде всего, я бы проверил RFC и спецификации ITU.T на наличие протокола, который делает то, что вы хотите.Если вы не найдете один, вы по крайней мере сможете увидеть, как были разработаны другие протоколы, и почитать за ним обоснование.

Посмотрите на XDR или BER (ASN.1) для двоичного кодирования данных.Есть библиотеки для того, что позаботится о беспорядке и выравнивании.Например, упаковка каждого пакета в непрозрачный XDR позволяет более эффективно проектировать сервер (1 интерфейсный модуль TCP, который отправляет необработанные непрозрачные запросы соответствующим обработчикам, не зная, что означает каждый пакет).Обязательно укажите, что должно происходить в особых случаях (искаженные пакеты, отсутствие ответа) или должны быть какие-либо пакеты поддержки активности?Если так, как часто?Должны ли быть некоторые клиент-серверные переговоры.

О, и не забудьте включить версию протокола в пакет приветствия.Получение тикета с вопросом «В моем клиентском приложении написано, что я не поддерживаю версию 2 протокола» лучше, чем «Некоторые клиенты работают нормально, но когда я пытаюсь получить набор данных, все, что я получаю, это случайные числа!».

3 голосов
/ 21 октября 2010

Вам нужен читабельный протокол?Если нет, то я бы предложил двоичный файл, начните с x байтов длины команды, а затем сформируйте оставшуюся часть сообщения, как считаете нужным;конечно, остальная часть сообщения может быть текстовой, если вы предпочитаете ... ИМХО, это НАМНОГО проще на сервере, так как вам не нужно сканировать все входные байты, чтобы определить, когда заканчивается сообщение.

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

Для защиты от атак типа "отказ в обслуживании" у вас должен быть тайм-аут для прочитанных данных.Нет полного сообщения в x, и вы отключаетесь.

Неправильное сообщение, вы отключаетесь.ИМХО Постель есть что ответить;ИМХО протоколы лучше, когда вы педантичны, что люди получают ПРАВИЛЬНО и не принимают ничего меньше ...

Размер сообщения больше, чем вы разрешаете, вы отключаетесь.

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

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

...