2015-06-29 5 views
1

Я пытаюсь прочитать big-endian 8 32-битных поплавков, передаваемых из упакованной структуры на поточном сервере Python tcp. Кажется, что он близок к работе, но первые несколько значений отключены нечетным количеством, а более поздние значения кажутся небольшими или просто неточными.Чтение поплавков с tcp

Например, эти значения интерпретируются клиентом:

Val[0] -1926.34 
Val[1] -1936.86 
Val[2] -1901.15 
Val[3] -1935.93 
Val[4] -148932 
Val[5] -145905 
Val[6] -41580.8 
Val[7] -134330 

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

Val[0] -7737.77159902711 
Val[1] -7746.444075875769 
Val[2] -7638.46279841218 
Val[3] -7776.037785534595 
Val[4] -148935.79768369172 
Val[5] -145903.3365134402 
Val[6] -41594.9200504923 
Val[7] -134328.9103304041 

Вот мой код:

int size = 32; 
char buffer[size]; 
float vals[8]; 
int count = 0; 
int t; 

// Receive a reply from the server 
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;} 

for (int i = 0; count < 8; i += 4, count++) { 
    t = 
      (buffer[i+3])  + 
      (buffer[i+2] << 8) + 
      (buffer[i+1] << 16) + 
      (buffer[i] << 24); 

    vals[count] = *reinterpret_cast<float*>(&t); 
} 

Сервер Python посылает пакет:

packer = struct.Struct('>%sf' % 8) 
packed_data = packer.pack(*values) 
sock.send(packed_data) 

У меня есть чувство, что это может быть проблемой, о преобразовании между Int и плавать, но Кажется, я не понимаю. Любая помощь приветствуется.

+0

Если у вас есть к нему доступ, было бы полезно, если бы вы могли добавить фрагмент Python, который отправляет данные. – icktoofay

+0

Спасибо, я добавлю. – jay

+0

Это не безопасный способ чтения данных: ваш буфер может быть недостаточно выровнен для '* reinterpret_cast (& t)' доступ не к SIGBUS на некоторых архитектурах. Было бы лучше «recv» непосредственно в/над массивом 'vals', используя, например, ['ntohl'] (http://linux.die.net/man/3/ntohl) и др. для окончательных преобразований, если это необходимо. Отдельно TCP не гарантирует, что один и тот же объем данных, указанных в вызове 'send', будет поступать сразу в' recv' - вы должны зацикливаться, пока не прочитаете ожидаемое количество байтов или не столкнетесь с ошибкой. –

ответ

1
  1. buffer использует char, который, скорее всего, подписанную в вашей системе.

    Если buffer[0] является -1, то, что преобразуется в -1 как int (больше не char). Это вызывает проблемы при объединении всех этих байтов (потому что -1 как char, скорее всего, 0xff, а в качестве int это, скорее всего, 0xffffffff). Другими словами, если буфер содержит байты с отрицательными значениями, это приведет к «слиянию» байтов из-за цельной рекламы.

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

Вы можете заменить его с кодом, который присваивает байты непосредственно к float с, как это:

int size = 32; 
char buffer[size]; 
float vals[8]; 
int count = 0; 
int t; 

// Receive a reply from the server 
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;} 

for (int i = 0; count < 8; i += 4, count++) { 
    char* ptr = (char*)(vals + count); 
    // switch endianness as needed (though unlikely) 
    // (this is effectively equivalent to std::memcpy(vals, buffer, size)) 
    ptr[0] = buffer[0]; 
    ptr[1] = buffer[1]; 
    ptr[2] = buffer[2]; 
    ptr[3] = buffer[3]; 
} 

Еще лучше (чтобы избежать ненужного копирования памяти), если вы знаете, два машины используют одни и те же плавающие форматы и размеры, вы можете просто сделать все это с recv:

// read the bytes directly into the floats 
recv(sock, vals, size, 0); 
+0

Я не уверен, как обойти ошибку приведения float * в char *, но мне нужно использовать этот метод, потому что мне нужно преобразовать его в маленький конец. – jay

+0

@jay : Извините, я всегда забываю, какой стиль стилей C++ использовать , Измените 'static_cast' на листинг C-стиля (или любой другой тип C++-стиля, который должен быть действительно). – Cornstalks

+0

Моя проблема сейчас, @cornstalks, заключается в том, что мне все еще нужно использовать ntohl для преобразования endianness. Однако я все еще не уверен, как это сделать. Я ненавижу просить, чтобы все было прописано, но не могли бы вы добавить функцию ntohl в этот код? Я провел довольно много времени и просто не могу обернуть вокруг себя голову. – jay

0

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

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

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

0
char buffer[size]; 
... 
t = 
     (buffer[i+3])  + 
     (buffer[i+2] << 8) + 
     (buffer[i+1] << 16) + 
     (buffer[i] << 24); 

Это неправильный способ байтового обмена 32-битным целым числом. Что делать, если char подписан? (Это так, если вы используете Windows, linux или OSX.)

Правильный способ - использовать функцию/макрос ntohl. Хотя это не является частью стандарта C или C++, вы найдете его в любой маленькой системе, которая, как ожидается, будет работать через Интернет. На машинах Unix вам нужно #include <arpa/inet.h>. В Windows вам необходимо #include <winsock2.h>.

Альтернатива не в том, чтобы причинить эту боль себе. В некотором смысле, это все, что вы делаете, используя packer = struct.Struct('>%sf' % 8) в вашем скрипте на python. Вы, по-видимому, знаете, что цель будет работать на маленькой конечной машине, поэтому измените > в конструкторе Struct на '<'. Если вы знаете, что скрипт python будет работать на том же компьютере, что и ваше приложение на C++, измените значение > на @ или =.

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