Должен ли я использовать #defines или перечисления для команд по сети? - PullRequest
0 голосов
/ 11 июля 2009

Я должен передать некоторые значения для использования в качестве команд по сети, и я хочу сделать его максимально эффективным и надежным, т. Е. Мне нужно мнения о том, что использовать для этих команд, определяет или перечисляет?

Диапазон команд не должен превышать 20 команд (скажем, 40 с каждым определенным ответом на команду), поэтому в соответствии с большинством вещей, которые я слышал, он хорошо вписался бы в предел символа.

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

Ответы [ 4 ]

4 голосов
/ 11 июля 2009

При передаче числовых значений через Интернет необходимо учитывать две вещи.

Первый - это порядок байтов, порядок, в котором появляются байты (или иногда биты). Предположим, у вас есть 32-битное значение 0xAABBCCDD. Процессоры Intel представляют собой машины с прямым порядком байтов, что означает, что эти байты будут храниться как {0xDD, 0xCC, 0xBB, 0xAA}. Другими словами, младший байт сохраняется по младшему адресу. В машине с прямым порядком байтов байты будут храниться как {0xAA, 0xBB, 0xCC, 0xDD}, причем младший байт будет иметь самый высокий адрес.

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

ntohl (сеть к хосту, длинная)
ntohs (сеть к хосту, короткая запись)
htonl (хост в сеть, длинный)
htons (хост в сеть, короткий)

Длинные версии работают с 32-разрядными целыми числами и короткие версии с 16-разрядными целыми числами. Пока вы всегда вызываете * hton ** перед передачей данных по сети и вызываете * ntoh ** при чтении из сети, данные будут в правильном порядке байтов.

Конечно, самый простой способ обойти эту проблему, особенно если у вас есть только 20 команд, это просто использовать одиночные байты или char s.

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

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

Например:

enum command_t { one, two, three }

void send_command(command_t c) {
    send((unsigned char)c);
}

command_t read_command() {
    return (command_t)recv();
}

void send(unsigned char c) { ... }
unsigned char recv() { ... }
1 голос
/ 11 июля 2009

Я бы также рекомендовал использовать текстовый протокол с разделителями строк. Помимо того, что вам не нужно беспокоиться о значениях enum -vs. #define, есть и другие преимущества:

хакерство / тестирование

Очень просто вручную создать набор команд для текстовых протоколов. Рассмотрим тестирование условий ошибки на HTTP-сервере:

$ telnet www.yahoo.com 80
Trying 69.147.76.15...
Connected to www-real.wa1.b.yahoo.com.
Escape character is '^]'.
GOAT / HTTP/1.1

Мне не нужно ничего необычного, чтобы понять, как сервер ответил:

HTTP/1.1 400 Bad Request
Date: Sat, 11 Jul 2009 16:20:59 GMT
Cache-Control: private
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1

Это отлично для отладки. Вы можете создать набор тестовых сценариев для подачи в telnet / nc, и их будет гораздо проще поддерживать, чем любые тестовые сценарии с двоичным протоколом.

Портативность

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

разделение

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

1 голос
/ 11 июля 2009

С точки зрения эффективности это не имеет никакого значения. #define или enum - это просто способ присвоения числового значения ... реальная хитрость заключается в том, как вы упаковываете это значение в сеть.

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

0 голосов
/ 11 июля 2009

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

Многие реальные протоколы основаны на тексте, и я осмелюсь сказать, что они довольно эффективны для того, что они делают. например, HTTP, FTP, SMTP и т. д. все текстовые.

...