Работа с шестнадцатеричными значениями в C / C ++ - PullRequest
0 голосов
/ 26 августа 2011

Я получаю значения с помощью winsock с другого компьютера в сети. Это сокет TCP с 4 первыми байтами сообщения, несущими его размер. Остальная часть сообщения форматируется сервером с использованием protobuf (буферы протокола от Google).

Проблема, я думаю, заключается в том, что, по-видимому, значения, отправляемые сервером, являются шестнадцатеричными значениями, передаваемыми в виде символов (т. Е. Только 10 получено для 0x10). Чтобы получить значения, я делаю это:

bytesreceived = recv(sock, buffer, msg_size, 0);
for (int i=0;i<bytesreceived;i++) 
{
    data_s << hex << buffer[i];
}

где data_s - поток строк. Я могу использовать метод ParseFromIstream (& data_s) из protobuf и восстановить нужную мне информацию.

Проблема, с которой я столкнулся, заключается в том, что это ОЧЕНЬ ОЧЕНЬ долго (я получил другую реализацию, использующую QSock, но я не могу использовать ее для своего проекта, но которая намного быстрее, поэтому на стороне сервера проблем нет).

Я пробовал много вещей, которые я взял отсюда и везде в интернете (используя массивы байтов, строки), но ничего не работает.

У меня есть другие варианты?

Спасибо за ваше время и комментарии;)

Ответы [ 5 ]

2 голосов
/ 26 августа 2011

не уверен, будет ли это полезным, но раньше я использовал аналогичный протокол (первые 4 байта содержат int с длиной, остальные кодируются с использованием protobuf), и для его декодирования я сделал что-то подобное (возможно не самое эффективное решение из-за добавления к строкам):

// Once I've got the first 4 bytes, cast it to an int:
int msgLen = ntohl(*reinterpret_cast<const int*>(buffer));

// Check I've got enough bytes for the message, if I have then 
// just parse the buffer directly
MyProtobufObj obj;
if( bytesreceived >= msgLen+4 )
{
  obj.ParseFromArray(buffer+4,msgLen);
}
else
{
  // just keep appending buffer to an STL string until I have 
  // msgLen+4 bytes and then do
  // obj.ParseFromString(myStlString)
}
0 голосов
/ 26 августа 2011

Какой тип данных buffer?

Все это выглядит как большой большой запрет, так как operator<<(stringstream&, char) игнорирует базовый спецификатор.Спецификатор hex влияет только на форматирование не символьных целочисленных типов.Наверняка вы не хотите передавать текстовые данные в protobuf. * ​​1008 *

Просто передайте указатель buffer на protobuf, все готово.

0 голосов
/ 26 августа 2011

Ваш вопрос довольно двусмысленный.Давайте последуем вашему примеру.Вы получаете 10 в виде символов и хотите получить это как шестнадцатеричное число.

Если recv даст вам эту строку символов, вы можете сделать это.

Прежде всего, сделайтеон завершается нулем:

bytesreceived[msg_size] = '\0';

тогда вы можете очень легко прочитать значение из этого буфера, используя стандартную функцию * scanf для строк:

int hexValue;
sscanf(bytesreceived, "%x", &hexValue);

Вот, пожалуйста!

Редактировать: Если вы получаете число в обратном порядке (например, 01 для 10), вероятно, ваш лучший способ - преобразовать его вручную:

int hexValue = 0;
int positionValue = 1;
for (int i = 0; i < msg_size; ++i)
{
    int digit = 0;
    if (bytesreceived[i] >= '0' && bytesreceived[i] <= '9')
        digit = bytesreceived[i]-'0';
    else if (bytesreceived[i] >= 'a' && bytesreceived[i] <= 'f')
        digit = bytesreceived[i]-'a';
    else if (bytesreceived[i] >= 'A' && bytesreceived[i] <= 'F')
        digit = bytesreceived[i]-'A';
    else // Some kind of error!
        return error;
    hexValue += digit*positionValue;
    positionValue *= 16;
}

Это всего лишь яркий пример.В действительности вы могли бы сделать это, например, с помощью сдвига битов, а не умножения.

0 голосов
/ 26 августа 2011

ОК, выстрел в темноту: допустим, ваш входной поток равен "71F4E81DA...", и вы хотите превратить его в поток байтов { 0x71, 0xF4, 0xE8, ...}. Затем мы можем просто собрать байты из литералов символов следующим образом, схематично:

char * p = getCurrentPointer();

while (chars_left() >= 2)
{
  unsigned char b;
  b  = get_byte_value(*p++) << 8;
  b += get_byte_value(*p++);

  output_stream.insert(b);
}

Здесь мы используем небольшую вспомогательную функцию:

unsigned char get_byte_value(char c)
{
  if ('0' <= c && c <= '9') return c - '0';
  if ('A' <= c && c <= 'F') return 10 + c - 'A';
  if ('a' <= c && c <= 'f') return 10 + c - 'a';

  return 0;  // error
}
0 голосов
/ 26 августа 2011

Я бы не использовал потоковые операторы. Они для форматированных данных, а это не то, что вы хотите.

Вы можете сохранить полученные значения в std :: vector с типом char (вектор байтов). По сути, это просто динамический массив. Если вы хотите продолжать использовать поток строк, вы можете использовать функцию stringstream :: write, которая принимает буфер и длину. У вас должен быть буфер и количество байтов, полученных от вашего вызова в recv.

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

#include <algorithm>
#include <iterator>
#include <vector>

char buf[256];
std::vector<char> bytes;
size_t n = recv(sock, buf, 256, 0);
std::copy(buf, buf + n, std::back_inserter(bytes));
...