2014-08-30 4 views
0

Я новичок в C, и я пытаюсь создать дейтаграмму UDP, а затем отправлять ее из клиентской программы в серверную программу на другой машине.Создание дейтаграммы UDP с заголовком фиксированного размера в C

У меня возникла проблема при попытке создать дейтаграмму, в частности заголовок фиксированного размера (IPv4): Формат заголовка (клиентский IP (32-разрядный) | клиентский порт (16 бит) | длина пакета (32 бит) | ... и т. Д.)

Подход, по которому я решил пойти, состоял в том, чтобы собрать необходимую информацию заголовка и тела, а затем передать их в char* и затем объединить их вместе для создания дейтаграммы. Тем не менее, когда я набрал int и по номеру char* (номер порта и длина пакета), он не гарантирует фиксированный размер char* (например, если длина пакета (длинный тип данных) вычисляется как 440, и я конвертирую его в char*, тогда strlen покажет длину char* как 2 вместо 4, так как число не достаточно велико, чтобы взять все 4 байта, я бы предположил ...)

Я пробовал использовать другой способ преобразования длинного символа фиксированного размера * по побитового операции:

//assume the datagram size was calculated to be 440 bits 
unsigned long len = 440; 
unsigned char dg_len [4]; 
dg_len[0] = (len >> 24) & 0xFF; 
dg_len[1] = (len >> 16) & 0xFF; 
dg_len[2] = (len >> 8) & 0xFF; 
dg_len[3] = len & 0xFF; 
printf("%d" ,strlen(dg_lenPtr)); //this would display 0 
printf("%d" ,(unsigned long)dg_lenPtr); //this display some random number 

Единственное объяснение, которое я думал для этого является то, что неиспользованного 2 высших б ytes из dg_lenPtr считываются как нулевой символ, поскольку strlen показывает длину, равную 0.

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

+1

'strlen' предназначен для * строк *, а не произвольных данных. –

+0

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

+0

Вам нужно будет решить проблемы с порядком байтового байта сети (big-endian) по сравнению с машинным байтом (малоприводным, если вы используете Intel). Вы можете использовать такие примитивы, как ['htons()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/htons.html) и 'htonl()' для кодирования 2-байтных и 4-байтовых значений в сетевом порядке байтов, а затем 'memmove()' или 'memcpy()' значения в ваш буфер заголовка (который, вероятно, лучше всего определяется как массив 'unsigned char'). –

ответ

0

Вы не ответственны за создание заголовка IPv4. Функции send() и sendto() обрабатывают это для вас. Все, на что вам нужно сосредоточиться, - отправить желаемую полезную нагрузку дейтаграммы на целевой IP-адрес назначения: (путем указания его непосредственно на sendto() или до connect() в случае send()).

int connect(int socket, const struct sockaddr *address, socklen_t address_len); 
ssize_t send(int sockfd, const void *buf, size_t len, int flags); 

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 
+0

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

0
unsigned long len = 440; 
unsigned char dg_len [4]; 
dg_len[0] = (len >> 24) & 0xFF; 
dg_len[1] = (len >> 16) & 0xFF; 
dg_len[2] = (len >> 8) & 0xFF; 
dg_len[3] = len & 0xFF; 

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

printf("%d" ,strlen(dg_lenPtr)); //this would display 0 

Вы не объявили dg_lenPtr, но при условии, что он является указателем на dg_len, называя strlen() на нем не имеет смысла, поскольку она не указывает на строку. Если len меньше 2 , strlen() вернет 0, потому что первый байт оказывается равным нулю.

printf("%d" ,(unsigned long)dg_lenPtr); //this display some random number 

Этот вывод может адрес хранится в dg_lenPtr, преобразованный в unsigned long (что бы это ни значило). Это может быть не так, потому что вы передаете unsigned long в printf(), который ожидает signed int. Результатом этого несоответствия является undefined поведение, что означает, что программа может делать все, что захочет.


Лично я использовал бы буфер для представления всей дейтаграммы и записи значений непосредственно в буфер. Вы можете использовать буфер фиксированного размера (unsigned char buf[/* some size*/];) или выделенный (unsigned char *buf = malloc (/* some size */);) в зависимости от ваших потребностей или предпочтений.

Для упрощения чтения и записи в буфер, я хотел бы создать некоторые вспомогательные функции:

static void w32 (unsigned char *b, unsigned long v) 
{ 
    b[0] = (v >> 24) & 0xff; 
    b[1] = (v >> 16) & 0xff; 
    b[2] = (v >> 8) & 0xff; 
    b[3] = (v  ) & 0xff; 
} 
static void w16 (unsigned char *b, unsigned v) 
{ 
    b[0] = (v >> 8) & 0xff; 
    b[1] = (v  ) & 0xff; 
} 

w32 (buf , ip); 
w16 (buf+4, port); 
w32 (buf+6, len); 
/* And so on */ 

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

Смежные вопросы