Лучший способ создать пакет - Байт за байтом? - PullRequest
2 голосов
/ 07 января 2010

Это связано с моим вопросом, заданным здесь сегодня на SO. Есть ли лучший способ создать пакет для отправки по последовательному каналу, а не делать это:

unsigned char buff[255];

buff[0] = 0x02
buff[1] = 0x01
buff[2] = 0x03

WriteFile(.., buff,3, &dwBytesWrite,..);

Примечание: У меня есть около двадцати команд для отправки, поэтому, если бы был лучший способ отправить эти байты на последовательное устройство более кратким способом, чем указывать каждый байт, это было бы отличный. Каждый байт является шестнадцатеричным, причем последний байт является контрольной суммой. Я должен уточнить, что я знаю, что мне придется указывать каждый байт для построения команд, но есть ли лучший способ, чем указывать каждую позицию массива?

Ответы [ 6 ]

8 голосов
/ 07 января 2010

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

#pragma push(pack)
#pragma pack(1)

struct FooHeader {
  uint someField;
  byte someFlag;
  dword someStatus;
};

#pragma pack(pop)

FooHeader hdr;
hdr.someField = 2;
hdr.someFlag = 3;
hdr.someStatus = 4;

WriteFile(..., sizeof(hdr), &hdr);
8 голосов
/ 07 января 2010

Вы можете инициализировать статические буферы следующим образом:

const unsigned char command[] = {0x13, 0x37, 0xf0, 0x0d};

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

2 голосов
/ 07 января 2010

Есть ли лучший способ собрать пакет, чем собирать его побайтово?

Да, но это потребует некоторых размышлений и тщательной разработки. Многие другие ответы говорят вам о других механизмах , с помощью которых вы можете собрать последовательность байтов в C ++. Но я предлагаю вам разработать абстракцию, представляющую часть пакета:

class PacketField {
    void add_to_packet(Packet p);
};

Затем вы можете определить различные подклассы:

  • Добавить в пакет один байт
  • Добавить 16-разрядное целое число в порядке с прямым порядком байтов. Еще один для байтов. Другие ширины кроме 16.
  • Добавить строку в пакет; закодируйте строку, вставив длину, а затем байты.

Вы также можете определить версию более высокого порядка:

PacketField sequence(PacketField first, PacketField second);

Возвращает поле, которое состоит из двух последовательных аргументов. Если вам нравится перегрузка оператора, вы можете перегрузить ее как + или <<.

Ваша базовая Packet абстракция будет просто расширяемой последовательностью байтов (динамический массив) с каким-то write методом.

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

Редактировать : Суть класса PacketField заключается в возможности комбинирования и повторного использования:

  • Составляя поля пакета, вы можете создавать более сложные поля пакета. Например, вы можете определить «добавить заголовок TCP» как функцию от PacketFields до PacketFields.

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

  • Вы можете создать подклассы PacketField, которые принимают дополнительные параметры.

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

1 голос
/ 07 января 2010

Существует гораздо лучший способ, который использует структуры для установки структур. Это обычно, как сетевые пакеты построены на низком уровне.

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

struct packet_header {
   int id;
   byte length;
   byte flags;
}; 

byte my_packet[] = new byte[100];
packet_header *header = &my_packet;
header->id = 20;
header->length = 10; // This can be set automatically by a function, maybe?
// etc.
header++; // Header now points to the data section.

Обратите внимание, что вам нужно убедиться, что структуры "упакованы", т.е. когда вы пишете byte length, это действительно занимает байт. Обычно вы достигаете этого, используя что-то вроде #pragma pack или подобное (вам нужно прочитать о настройках прагмы вашего компилятора).

Также обратите внимание, что вам, вероятно, следует использовать функции для выполнения общих операций. Например, создайте функцию, которая получает в качестве входных данных размер, данные для отправки и другую информацию и заполняет для вас заголовок пакета и данные. Таким образом, вы можете выполнять вычисления относительно фактического размера, который вы хотите записать в поле длины, вы можете вычислить CRC внутри функции и т. Д.

Редактировать: Это C-центричный способ работы, стиль большого сетевого кода. Более ориентированный на C ++ (объектно-ориентированный) подход также мог бы работать, но я менее знаком с ними.

1 голос
/ 07 января 2010

Да, есть лучший метод. Пусть ваши классы будут читать и записывать в упакованный буфер. Вы могли бы даже реализовать это как интерфейс. Шаблоны помогут.

Пример написания:

template <typename Member_Type>
void Store_Value_In_Buffer(const Member_Type&, member,
                           unsigned char *& p_buffer)
{
  *((Member_Type *)(p_buffer)) = member;
  p_buffer += sizeof(Member_Type);
  return;
}

struct My_Class
{
    unsigned int datum;

    void store_to_buffer(unsigned char *& p_buffer)
    {
      Store_Value_In_Buffer(datum, buffer);
      return;
    }
};

//...
unsigned char    buffer[256];
unsigned char *  p_buffer(buffer);
MyClass object;
object.datum = 5;
object.store_to_buffer(p_buffer);
std::cout.write(p_buffer, 256);

Часть интерфейса также запрашивает у объектов размер, который они занимают в буфере, скажем, метод size_in_buffer. Это оставлено в качестве упражнения для читателя. : -)

0 голосов
/ 07 января 2010

const char * c = "\ x02 \ x02 \ x03";

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