Кодирование, декодирование целого числа в массив символов - PullRequest
1 голос
/ 09 ноября 2009

Обратите внимание, что это не домашняя работа, и я выполнил поиск перед началом новой темы Я получил Сохранить int в массиве символов?

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

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

Вот часть кодирования:

const int MAX=5;
uint32_t a = 0xff00ffaa;
char byte_array[1024]; // this is the array to be transferred over the network
char buff[MAX]="";
sprintf(buff,"%4d",a);
memcpy(byte_array,buff,4);
// fill remaining stuff in the byte array and send it over the network

Вот часть декодирования:

const int MAX=5;
char buff[MAX]="";
strncat(buff,byte_array,4)

int i=atoi(buff);
// Work with i

Вот мои вопросы:

1) Является ли приведенный выше код переносимым? Я думаю, что это (пожалуйста, поправьте меня)

2) Теперь я хочу закодировать байтовый массив с 3 байтами (но целочисленный размер равен 4), т.е. сказать, что целое число хранит 0x00ffaabb, и я просто хочу, чтобы массив байтов имел ff int 0-й индекс aa в 1-м индексе и BB во втором индексе. Как это сделать?

snprinf не работает или, может быть, я что-то упустил.

Человек, который внедрил любой сетевой протокол, может легко помочь мне. Я думаю, что логика декодирования все еще будет работать. (strncat(buff,byte_array,3) с последующим atoi вызовом функции).

Вот что написано в протоколе:

    --------+--------+--------+--------+------------------------------
    |Version|   3       byte    length |  Remaining stuff
    --------+--------+--------+--------+------------------------------

Версия составляет 1 байт, за которой следует 3-байтовая длина сообщения.

Я надеюсь, что смогу прояснить мою проблему

Ответы [ 10 ]

6 голосов
/ 09 ноября 2009

Вы сохраняете как ASCII, где вы должны хранить сами байты.

Кодировка должна выглядеть примерно так:

uint32_t a = 0xff00ffaa;
unsigned char byte_array[1024];

Обратите внимание, как я сделал ваш целевой массив без знака, чтобы указать, что это «необработанные байты», а не символы.

byte_array[0] = a >> 24;
byte_array[1] = a >> 16;
byte_array[2] = a >> 8;
byte_array[3] = a >> 0;

Сериализует переменную a в четыре первых байта byte_array, используя порядок байтов с прямым порядком байтов, который является своего рода значением по умолчанию для многих сетевых протоколов.

Вы также можете увидеть мой ответ здесь: вопрос 1577161 .

4 голосов
/ 09 ноября 2009

1) это своего рода работа, так как вы используете массив символов для транспортировки, я бы лично использовал двоичный протокол. Если вы можете использовать 4 байта вашей переменной, я бы посмотрел на функции htonl / ntohl (они есть практически на каждом unix и на windows начиная с w2k), иначе смотрите ниже

2) с двоичным протоколом, кодировка будет

uint32_t a = 0xff00ffaa;
char byte_array[1024]; // this is the array to be transferred over the network

// leave byte_array[0] for version byte
// leave the high order byte in a since you want only the 3 lowest
byte_array[1] = (char)((a & 0x00FF0000)>>16);
byte_array[2] = (char)((a & 0x0000FF00)>>8);
byte_array[3] = (char)(a & 0x000000FF);

и расшифровка будет

uint32_t a = 0;
a |= byte_array[1]<<16;
a |= byte_array[2]<<8;
a |= byte_array[3];
2 голосов
/ 09 ноября 2009

То, что вы делаете, будет работать. Вы не передаете байты данных - вы передаете числовое значение данных. В результате размер буфера 5 слишком мал для отправляемых данных (0xFF00FFAA имеет числовое значение 4278255530 - 10 байт).

Чтобы передать байты, вам нужно сделать что-то вроде следующего (предполагается, что в порядке байтов):

Encode:

char array[1024]; // outgoing network data
int next = 0;

array[next++] = value & 0xFF;
array[next++] = (value >> 8) & 0xFF;
array[next++] = (value >> 16) & 0xFF;
array[next++] = (value >> 24) & 0xFF;

Эти операторы убирают байты значения и присваивают их последовательным значениям в вашем массиве.

Decode:

char array[1024]; // incoming network data
int next = 0;

value = 0;
value |= (int)*((unsigned char*)array)[next++];
value |= (int)*((unsigned char*)array)[next++] << 8;
value |= (int)*((unsigned char*)array)[next++] << 16;
value |= (int)*((unsigned char*)array)[next++] << 24;

Эти операторы вытаскивают байты из массива и возвращают их обратно в значение.

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

1 голос
/ 09 ноября 2009

Использование XDR ( RFC 4506 ).

1 голос
/ 09 ноября 2009

Может быть, вам нужно заставить это работать с существующим протоколом, в этом случае игнорируйте мой ответ.

Вместо того, чтобы заново изобретать колесо, почему бы вам не использовать библиотеку Google Protocol Buffers для этой работы? Проще, гибче и эффективнее.

1 голос
/ 09 ноября 2009

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

Вам действительно нужно реализовать новый сетевой протокол обмена сообщениями? Вас не устраивает НАСА IPC или Sun RPC? Они оба достаточно стабильны, NASA проще для запуска, RPC кажется более доступным (да, он готов к использованию и библиотека доступна для большинства популярных систем).

  • Для RPC попробуйте 'man rpc'
  • Для НАСА IPC смотрите здесь
0 голосов
/ 03 февраля 2019

Я посмотрел эту страницу миллион раз, и я действительно ценю все остальные ответы за помощь. Вот заглушка, которую я использую, которая уникальна среди других ответов, потому что ее можно использовать в цикле for:

void encode_int_as_char(int num, char *buf, int length){
    int i;
    for (i = 0; i < length; i++){
        buf[i] = (char)(num >> ((8 * (length - i - 1)) & 0xFF));
    }
}

int decode_int_from_char(char *enc, int length){
    int i, num, cur;

    num = 0;
    for (i = 0; i < length; i++){
        cur = (unsigned char) enc[i] << (8 * (length - i - 1));
        num += (int) cur;
    }

    return num;
}
0 голосов
/ 09 ноября 2009

Использование функции atoi оправдано только в том случае, если строка, которую вы ожидаете декодировать, была собрана вашим собственным кодом и не более чем на пару строк выше. Т.е. его можно использовать только в набросковом коде.

В противном случае, особенно в вашем случае, когда данные поступают из сети, функция atoi не может быть осмысленно использована для выполнения декодирования, поскольку она не обеспечивает полезного механизма обработки ошибок и абсолютно не защищает от переполнения (неопределенное поведение при переполнении) , Единственная функция, которую можно использовать для преобразования строки в целочисленное значение, - это функция из группы strto..., strtol в вашем случае.

0 голосов
/ 09 ноября 2009

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

В противном случае вы можете просто сделать что-то вроде ...

unsigned char msg[1024];
int writeIndex = 0;
[...]
int mynum  = 12345;
memcpy(msg + writeIndex , &mynum, sizeof mynum);
writeIndex += sizeof mynum;

и расшифровать

//[...] also declare readIndex;
memcopy(&mynum, msg + readIndex, sizeof mynum);
readIndex += sizeof mynum;

(вы можете заменить понятие msg + index на указатель без знака, хотя это вряд ли имеет значение).

Использование memcpy подобным образом может быть медленнее, но также более читабельным. При необходимости вы можете реализовать клон memcopy в функции #define или inline - в конце концов, это всего лишь короткий цикл назначений.

0 голосов
/ 09 ноября 2009

То, что у вас есть, не будет работать так, как вы это имеете. Например, a является 32-битным, и в вашем примере вы установили старшие биты, что означает, что он не может вписаться в 4-значное число с вашим оператором printf. (0xff00ffaa = 4278255530, что больше 4 цифр) Я полагаю, что это переполнит буфер. Я считаю, что printf преобразует его и переполняет поле, но это зависит от того, как ваш компилятор / C реализует функцию printf, когда не хватает места в буфере.

Для вашего оператора printf максимальное значение, которое вы можете передать, будет 9999 для 4 символов. Аналогично, в вашем примере передачи данных с полем длины 3 байта максимальная длина будет 999. Теоретически ваша длина может быть 1000, если вы добавите 1 к длине, но объявленный вами буфер равен 1024. где максимальная необходимая длина буфера будет равна 1004 байта.

Использование символов ASCII делает сообщения / данные переносимыми по всей системе, но это происходит за счет использования большей полосы пропускания / пространства, а также времени и усилий по программированию для преобразования туда и обратно из ASCII для передачи данных.

Кажется, у вас есть хорошая идея, но она все еще требует небольшой работы.

...