LINUX.ORG.RU

byte order (htonl)


0

0

нужно по сети отправить данные, в конкретном случае строка (char *) 
Соотвественно необходимо конвертировать данные в формат сети (htonl). Делаю так (упрошенно):

char *buffer;
int size;

//some code

msend(buffer,size);

int
msend(void *buffer,size){
  uint32t *tmp;
  tmp=(uint32t *)buffer;
  for(...)
    tmp[i]=htonl(tmp[i]);
  //sending ...
}

смутные сомнения одолевают меня: проблем не будет с "выравниванием" при tmp=(uint32t *)buffer ?
anonymous

RE:

С выравниванием чего? 32 битового слова в памяти?

На Intel не будет, на других машинах, если сумеешь передать невыровненый по 32 бита buffer - будет исключение выравнивания при попытке разыменовать указатель (предположительно SIGBUS).

Murr ★★
()

седня в одной книге читал (из-за этого возникли сомнения) что:
char может быть расположен по какому угодно адресу (четный/не четный)
iny по четным адресам,
в итоге получается что:

char *buffer;
int *p;

p=(int *)buffer;

получаем два случая:
1. если buffer находится по четному адресу, то все ок
2. иначе p будет улазывать на соседний с buffer адрес

anonymous
()
Ответ на: комментарий от anonymous

RE:

На Intel понятие выравнивания имеет смысл только для оценки эффективности доступа и атомарности чтения/изменения и для очень специфичных команд.

В общем же случае на Intel для любых типов данных допустимо любое выравнивание.

P.S. Так о какой архитектуре идет речь? Если речь только об Intel, то не парь себе мозги этим, всё будет работать. Если хочешь переносимый код, то так писать нельзя...

Murr ★★
()

RE:

Хотя, что интересно, посмотрел только что: на Alpha Linux в обработчике исключения эмулирует доступ к невыровненным данным через выровненный доступ, но, видимо, не для всех инструкций, и полагаться на это не следует ...

Murr ★★
()

в конкретном случае речь о архитектуре Intel. Могли-бы показать пример переносимого кода?

anonymous
()

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

anonymous
()
Ответ на: комментарий от anonymous

RE:

>в конкретном случае речь о архитектуре Intel. Могли-бы показать пример переносимого кода?

Тут в общем идея примерно такая: ответственен за создание работающего кода компилятор. Нормальный компилятор протранслирует корректный C код в корректный бинарный код этой платформы. Т.е. с учетом специфики платформы компилятор всегда разместит данные в памяти выровненными. Проблемы начинаются при нарушении стандарта C, например при приведении (char *) или (void *) к (int *) - тут нормальный компилятор при компиляции должен предупредить, что используется некорректное приведение типов и программа может работать некорректно (для многих платформ возможно нарушение выравнивания).

Что касается самой передачи данных, то по-моему проще всего сделать примерно так:

int marshal_uint32 (int stream, uint32_t l) {
uint32_t nl = htonl (l);

return (write (stream, &nl, sizeof(nl)) == sizeof(nl));
}

int marshal_uint16 (int stream, uint16_t s) {
uint16_t ns = htons (s);

return (write (stream, &ns, sizeof(ns)) == sizeof (nl));
}

Тогда marshalling для структуры
struct {
uint32_t a;
uint16_t b;
} str_t;

Можно сделать так:

int marshal_str (int stream, str_t * s) {
return marshal_uint32(stream, s->a) && marshal_uint16(stream, s->b);
}


demarshalling аналогично:

int demarshal_uint32 (int stream, uint32_t * l) {
uint32_t nl;

if (read (stream, &nl, sizeof(nl)) != sizeof(nl)) return 0;
*l = ntohl (nl);
return 1;
}

int demarshal_uint16 (int stream, uint16_t * s) {
uint16_t ns;

if (read (stream, &ns, sizeof(ns)) != sizeof(ns)) return 0;
*s = ntohs (ns);
return 1;
}

int demarshal_str (int stream, str_t * s) {
return demarshal_uint32(stream, &s->a) && demarshal_uint16(stream, &s->b);
}


Если где-то и проглючил в деталях, то суть, думаю, всё равно понятна.

Murr ★★
()

RE:

В gcc, к сожалению, контроль некорректного приведения типов не входит в -Wall, нужно добавлять -Wcast-align.

Murr ★★
()

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "defs.h"

// реально buffer типа char *
int
msend (int socket_d, void *buffer, size_t size)
{

  int size_in_uint32, i;
  uint32_t *tmp;

  size_in_uint32 = size / sizeof (uint32_t);
  tmp=(uint32_t *)buffer;
  for (i = 0; i < size_in_uint32; i++)
    tmp[i] = htonl (tmp[i]);

  return send (socket_d, buffer, size, 0);
}

gcc -W -Wall -Wtraditional -W -pedantic -ansi -Wcast-align -c defs.c

ни одного сообшения об ошибке, плохо однако ;)

anonymous
()
Ответ на: комментарий от anonymous

RE:

Да, почему-то для void * не рюхает gcc :(

Впрочем, void * изначально кривой тип. IMHO, использовать его не стоит.

А если так:

[root@blackbox /root]# cat test.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
// реально buffer типа char *
int
msend (int socket_d, char *buffer, size_t size)
{

  int size_in_uint32, i;
  uint32_t *tmp;

  size_in_uint32 = size / sizeof (uint32_t);
  tmp=(uint32_t *)buffer;
  for (i = 0; i < size_in_uint32; i++)
    tmp[i] = htonl (tmp[i]);

  return send (socket_d, buffer, size, 0);
}
int main () {
        return 0;
}
[root@blackbox /root]# gcc -Wall -Wcast-align test.c
test.c: In function `msend':
test.c:13: warning: cast increases required alignment of target type
[root@blackbox /root]#  cat /proc/cpuinfo
cpu                     : Alpha
cpu model               : EV67
cpu variation           : 7
cpu revision            : 0
cpu serial number       :
system type             : Nautilus
system variation        : 0
system revision         : 0
system serial number    :
cycle frequency [Hz]    : 598802395
timer frequency [Hz]    : 1024.00
page size [bytes]       : 8192
phys. address bits      : 44
max. addr. space #      : 255
BogoMIPS                : 1187.96
kernel unaligned acc    : 0 (pc=0,va=0)
user unaligned acc      : 1 (pc=120000424,va=11ffffa01)
platform string         : API UP1000 598 MHz
cpus detected           : 1

Murr ★★
()

я и с char для интереса пробовал - результат тот же - ноль предупреждений

bash-2.05b$ gcc-3.2 -Wall -W -Wcast-align -c defs.c 
bash-2.05b$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-linux/3.2.3/specs
Configured with: ../src/configure -v --enable-languages=c,c++,f77,objc,ada --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-gxx-include-dir=/usr/include/c++/3.2 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --enable-__cxa_atexit --enable-clocale=gnu --enable-objc-gc i386-linux
Thread model: posix
gcc version 3.2.3 (Debian)
bash-2.05b$ 

Теоретически, разницы быть не должно между void * и char *, потому как у char * нет проблем с padding

anonymous
()
Ответ на: комментарий от anonymous

gcc делает код для конкретной платформы, для Intel нет понятия alignment, поэтому я и написал cat /proc/cpuinfo в примере.

А с void * непонятно ... надо мануалы читать :)

Murr ★★
()

Не, вы че издеваетесь? Какой еще порядок байт может быть для типа char?

anonymous
()

little/bif endian

anonymous
()
Ответ на: комментарий от anonymous

>Не, вы че издеваетесь? Какой еще порядок байт может быть для типа char?
Вроде как об uint32_t шла речь :)

Murr ★★
()

Ну в условии сказано: передать строку, а строка - набор 1 байтных символов ;-), так что в данном случае все эти издевательства с htonl не только не нужны, а даже вредны

anonymous
()
Ответ на: комментарий от anonymous

Да, что-то в первом сообщении напутано. :)

Ну я как бы отвечал насчет uint32_t и uint16_t, а так - понятно, что нет никакой проблемы передачи байт :)

Murr ★★
()

действительно зачем я делал ntohl? Просто забыл сказать что буфер состоит из целых чисел + строка.

Кроме этого, интересовала вообше проблема :

int
msend(void *buffer,size){
  uint32_t *tmp;
  tmp=(uint32_t *)buffer; <===== что происxодит тут
...
}

anonymous
()

Вообщето порядок байт нужно учитывать только при задании адреса назначения/привязки,
при передачи данных стек tcp/ip сам заботится о переконвертировании.
Это описано у Стивенса, и в описании ISO/OSI.

OxiD ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.