Создание и отправка пакетов данных в C / C ++ - PullRequest
14 голосов
/ 26 апреля 2011

Допустим, я хочу отправить следующие данные в сокет с использованием C или C ++, все в одном пакете:

Headers
-------
Field 1: 2 byte hex
Field 2: 2 byte hex
Field 3: 4 byte hex

Data
----
Field1 : 2 byte hex
Field1 : 8 byte hex

Как обычно будет выглядеть код для создания и отправки пакета, содержащего все эти данные?

Ответы [ 4 ]

17 голосов
/ 27 апреля 2011

Предположим, что ваша программа уже организована, чтобы иметь заголовок в одном struct и данные в другом struct. Например, у вас могут быть следующие структуры данных:

#include <stdint.h>
struct header {
    uint16_t f1;
    uint16_t f2;
    uint32_t f3;
};
struct data {
    uint16_t pf1;
    uint64_t pf2;
};

Давайте назовем эту организацию "формат хоста". Мне действительно не важно, какой у вас формат хоста, если он полезен для остальной части вашей программы. Давайте назовем формат, который вы передадите send(), вызовом «сетевой формат». (Я выбрал эти имена, чтобы они соответствовали именам htons (от хоста к сети) и htonl (от хоста до сети).)

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

#include <arpa/inet.h>
#include <string.h>
void htonHeader(struct header h, char buffer[8]) {
    uint16_t u16;
    uint32_t u32;
    u16 = htons(h.f1);
    memcpy(buffer+0, &u16, 2);
    u16 = htons(h.f2);
    memcpy(buffer+2, &u16, 2);
    u32 = htonl(h.f3);
    memcpy(buffer+4, &u32, 4);
}
void htonData(struct data d, char buffer[10]) {
    uint16_t u16;
    uint32_t u32;
    u16 = htons(d.pf1);
    memcpy(buffer+0, &u16, 2);
    u32 = htonl(d.pf2>>32);
    memcpy(buffer+2, &u32, 4);
    u32 = htonl(d.pf2);
    memcpy(buffer+6, u32, 4);
}
void htonHeaderData(struct header h, struct data d, char buffer[18]) {
    htonHeader(h, buffer+0);
    htonData(d, buffer+8);
}

Чтобы отправить свои данные, сделайте это:

...
char buffer[18];
htonHeaderData(myPacketHeader, myPacketData, buffer);
send(sockfd, buffer, 18, 0);
...

Опять же, вам не нужно использовать структуры header и data, которые я определил. Просто используйте все, что нужно вашей программе. Ключевым моментом является то, что у вас есть функция преобразования, которая записывает все данные с четко определенными смещениями, в четко определенном порядке байтов в буфер и передает этот буфер в функцию send ().

На другой стороне сетевого соединения вам понадобится программа для интерпретации полученных данных. На этой стороне вам нужно написать соответствующие функции (ntohHeader и т. Д.). Эти функции будут memcpy выводить биты из буфера в локальную переменную, которую она может передавать в ntohs или ntohl. Я оставлю эти функции на ваше усмотрение.

5 голосов
/ 26 апреля 2011

Ну, обычно это выглядит так, как будто он подготавливает эту структуру пакета в буфер памяти (делая разумные вызовы семейства функций htonl).

Если бы тогда использовались функции send, sendto, sendmsg или write, надеюсь, с большой осторожностью обращая внимание на длину буфера и хорошую обработку ошибок / составление отчетов.

(или один из API-интерфейсов Win32 для отправки, если это целевой планшет).

Хорошую презентацию обо всем этом вы найдете в Руководство Beej по сетевому программированию .

Специально для части упаковки байтов (с учетом порядка байтов) посмотрите тему serialization . (В этом разделе гораздо больше деталей, чем нужно для простых целочисленных типов данных фиксированного размера.

2 голосов
/ 26 апреля 2011

Код будет выглядеть по-разному в зависимости от сетевой библиотеки ОС (* nix использует сокеты Беркли , Windows использует Winsock и т. Д.). Однако вы можете создать структуру, содержащую все данные, которые вы хотите отправить в пакете, например,

typedef struct
{
    short field1;
    short field2;
    int field3;
} HeaderStruct;

typedef struct
{
    short field1;
    long long field2;
} PacketDataStruct;

в предположении 32-битного целого размера.

Edit:

Как кто-то любезно напомнил мне в комментариях, не забудьте о преобразовании в Сетевой заказ . Сетевые библиотеки будут иметь такие функции, как ntohs, nothl, htons и htonl.

.
1 голос
/ 26 апреля 2011

Один простой ответ заключается в том, что он будет отправлен в том формате, который ожидает получатель.Это немного напрашивается на вопрос, хотя.Предполагая, что данные имеют фиксированный размер, как показано, и получатель ожидает, что вы могли бы использовать упакованную (1-байтовое выравнивание) структуру и сохранить данные в каждом поле.Причина использования однобайтового выравнивания заключается в том, что обычно легче убедиться, что оба конца ожидают одинаковых данных.Без 1-байтового выравнивания структура, возможно, будет выглядеть по-разному в зависимости от параметров компилятора, 32-битной или 64-битной архитектуры и т. Д.) И, как правило, ожидается, что вы отправите значения в сетевом байтовом порядке, если шестнадцатеричные значенияцелые числа.Вы можете использовать такие функции, как htons и htonl (и, возможно, htobe64, если доступно), чтобы преобразовать их.

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

ret = send( socket, &mystruct, sizeof( mystruct ), 0 );

Это предполагает, что mystruct объявлен как экземпляр структуры, а не указатель на структуру.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...