Что такое переносимый способ преобразования байтовых порядков строк в C

Я пытаюсь написать сервер, который будет связываться с любым стандартным клиентом, который может создавать соединения сокетов (например, клиент telnet)

Он начинался как эхо-сервер, что, конечно, не нужно было беспокоиться об упорядочивании сетевых байтов.

Я знаком с ntohs, ntohl, htons, htonl функциями. Они были бы велики сами по себе, если бы я переносил либо 16 или 32-битные int, либо если символы в отправляемой строке были кратными 2 или 4 байтам.

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

str_ntoh(char* net_str, char* host_str, int len) { uint32_t* netp, hostp; netp = (uint32_t*)&net_str; for(i=0; i < len/4; i++){ hostp[i] = ntoh(netp[i]); } } 

Или что-то подобное. Вышеприведенная вещь предполагает, что словосочетание является 32-битным. Мы не можем быть уверены, что слова на отправляющей машине не являются 16-битными или 64-битными?

Для клиентских программ, таких как telnet, они должны использовать hton * перед отправкой и ntoh * после получения данных, исправить?

EDIT: для людей эта вещь, потому что 1-char – это байт, который не имеет значения для endian-ness:

 int main(void) { uint32_t a = 0x01020304; char* c = (char*)&a; printf("%x %x %x %x\n", c[0], c[1], c[2], c[3]); } 

Запустите этот fragment кода. Выход для меня выглядит следующим образом:

 $ ./a.out 4 3 2 1 

Те, что на чипсетах powerPC должны получить «1 2 3 4», но те из нас, на чипсете Intel, должны увидеть, что я получил выше по большей части.

Может быть, я здесь что-то пропустил, но вы отправляете строки, то есть последовательности символов? Тогда вам не нужно беспокоиться о порядке байтов. Это только для битовой диаграммы в целых числах. Символы в строке всегда находятся в «правильном» порядке.

РЕДАКТИРОВАТЬ:

Деррик, чтобы ответить на ваш пример кода, я запустил следующую (слегка расширенную) версию вашей программы на Intel i7 (мало-endian) и на старой Sun Sparc (big-endian)

 #include  #include  int main(void) { uint32_t a = 0x01020304; char* c = (char*)&a; char d[] = { 1, 2, 3, 4 }; printf("The integer: %x %x %x %x\n", c[0], c[1], c[2], c[3]); printf("The string: %x %x %x %x\n", d[0], d[1], d[2], d[3]); return 0; } 

Как вы можете видеть, я добавил массив символов в массив для распечатки целого числа.

Выход из малоинтенсивного Intel i7:

 The integer: 4 3 2 1 The string: 1 2 3 4 

И выход с большого конца Солнца:

 The integer: 1 2 3 4 The string: 1 2 3 4 

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

С вашей подписью функции, как вы отправили, вам не нужно беспокоиться о порядке байтов. Он принимает char *, который может обрабатывать только 8-битные символы. С одним байтом на символ вы не можете иметь проблему с байтовым порядком.

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

Если вы хотите отправить их в виде 8-битной кодировки (тот факт, что вы используете char это означает, что вы хотите), нет необходимости байтовать swap. Однако для несвязанной проблемы с символами, отличными от ASCII, так что один и тот же символ > 127 отображается на обоих концах соединения одинаково, я бы предположил, что вы отправляете данные в нечто вроде UTF-8 , которое может представлять все символы Unicode и могут быть безопасно обработаны как строки ASCII. Способ получить текст UTF-8, основанный на кодировке по умолчанию, зависит от платформы и набора библиотек, которые вы используете.

Если вы отправляете 16-битную или 32-битную кодировку … Вы можете включить один символ с отметкой порядка байтов, которую другой конец может использовать для определения сущности символа. Или вы можете принять сетевой порядок байтов и использовать htons() или htonl() как вы предлагаете. Но если вы хотите использовать char , см. Предыдущий абзац. 🙂

Мне кажется, что прототип функции не соответствует его поведению. Вы передаете символ char *, но затем вы отправляете его на uint32_t *. И, более внимательно, вы выбрасываете адрес указателя, а не содержимое, поэтому я обеспокоен тем, что вы получите неожиданные результаты. Возможно, следующее будет работать лучше:

 arr_ntoh(uint32_t* netp, uint32_t* hostp, int len) { for(i=0; i < len; i++) hostp[i] = ntoh(netp[i]); } 

Я основываю это на предположении, что у вас действительно есть массив uint32_t, и вы хотите запустить ntoh () для всех из них.

Надеюсь, это полезно.