Передача значений с плавающей точкой по TCP / IP и повреждение данных - PullRequest
3 голосов
/ 20 сентября 2010

У меня очень странная ошибка.

У меня есть два приложения, которые взаимодействуют по TCP / IP.

Приложение A является сервером, а приложение B является клиентом.

Приложение A отправляет набор значений с плавающей запятой в приложение B каждые 100 миллисекунд.

Ошибка заключается вследующее: иногда некоторые значения с плавающей точкой, полученные приложением B, не совпадают со значениями, передаваемыми приложением A.

Первоначально я думал, что возникла проблема с драйверами Ethernet или TCP / IP (своего родаповреждение данных).Затем я проверил код на других машинах Windows, но проблема осталась.

Затем я проверил код на Linux (Ubuntu 10.04.1 LTS), и проблема все еще существует !!!

Значения регистрируются непосредственно перед отправкой и сразу после их получения.

Код довольно прост: протокол сообщения имеет 4-байтовый заголовок, подобный следующему:

//message header
struct MESSAGE_HEADER {
    unsigned short type;
    unsigned short length;
};

//orientation message
struct ORIENTATION_MESSAGE : MESSAGE_HEADER
{
  float azimuth;
  float elevation;
  float speed_az;
  float speed_elev;
};

//any message
struct MESSAGE : MESSAGE_HEADER {
    char buffer[512];
};

//receive specific size of bytes from the socket
static int receive(SOCKET socket, void *buffer, size_t size) {
    int r;
    do {
        r = recv(socket, (char *)buffer, size, 0);
        if (r == 0 || r == SOCKET_ERROR) break;
        buffer = (char *)buffer + r;
        size -= r;
    } while (size);
    return r;
}

//send specific size of bytes to a socket
static int send(SOCKET socket, const void *buffer, size_t size) {
    int r;
    do {
        r = send(socket, (const char *)buffer, size, 0);
        if (r == 0 || r == SOCKET_ERROR) break;
        buffer = (char *)buffer + r;
        size -= r;
    } while (size);
    return r;
}

//get message from socket
static bool receive(SOCKET socket, MESSAGE &msg) {
    int r = receive(socket, &msg, sizeof(MESSAGE_HEADER));
    if (r == SOCKET_ERROR || r == 0) return false;
    if (ntohs(msg.length) == 0) return true;
    r = receive(socket, msg.buffer, ntohs(msg.length));
    if (r == SOCKET_ERROR || r == 0) return false;
    return true;
}

//send message
static bool send(SOCKET socket, const MESSAGE &msg) {
    int r = send(socket, &msg, ntohs(msg.length) + sizeof(MESSAGE_HEADER));
    if (r == SOCKET_ERROR || r == 0) return false;
    return true;
}

Когда яПри получении сообщения «ориентация» иногда значение «азимута» отличается от значения, отправленного сервером!

Разве данные не должны быть одинаковыми все время?TCP / IP не гарантирует доставку данных без повреждений?Может ли быть так, что исключение в математическом сопроцессоре влияет на стек TCP / IP?проблема в том, что я сначала получаю небольшое количество байтов (4 байта), а затем тело сообщения?

РЕДАКТИРОВАТЬ:

Проблема заключается в процедуре замены порядка байтов.Следующий код меняет местами порядковый номер определенного числа с плавающей запятой, а затем меняет его снова и печатает байты:

#include <iostream>
using namespace std;

float ntohf(float f)
{
    float r;
    unsigned char *s = (unsigned char *)&f;
    unsigned char *d = (unsigned char *)&r;
    d[0] = s[3];
    d[1] = s[2];
    d[2] = s[1];
    d[3] = s[0];
    return r;
}

int main() {
    unsigned long l = 3206974079;
    float f1 = (float &)l;
    float f2 = ntohf(ntohf(f1));
    unsigned char *c1 = (unsigned char *)&f1;
    unsigned char *c2 = (unsigned char *)&f2;
    printf("%02X %02X %02X %02X\n", c1[0], c1[1], c1[2], c1[3]);
    printf("%02X %02X %02X %02X\n", c2[0], c2[1], c2[2], c2[3]);
    getchar();
    return 0;
}

Вывод:

7F 8A 26 BF 7F CA 26 BF

Т.е. присвоение с плавающей точкой, вероятно, нормализует значение, производя значение, отличное от оригинала.

Любые входные данные для этого приветствуются.

EDIT2:

Спасибовы все за ваши ответы.Кажется, проблема в том, что подкачка с плавающей запятой, когда возвращается с помощью оператора return, помещается в стек с плавающей запятой ЦП.Затем вызывающая сторона извлекает значение из стека, значение округляется, но это перестановка с плавающей точкой, и, следовательно, округление портит значение.

Ответы [ 2 ]

3 голосов
/ 20 сентября 2010

TCP пытается доставить неизмененные байты, но если машины не имеют одинаковые ЦП и операционные системы, нет гарантии, что представление с плавающей точкой в ​​одной системе идентично представлению в другой.Вам нужен механизм для обеспечения этого, такой как XDR или протобуф Google.

0 голосов
/ 20 сентября 2010

Вы отправляете двоичные данные по сети, используя заполнение, определяемое реализацией, для структуры структуры, поэтому это будет работать только в том случае, если вы используете одно и то же оборудование, ОС и компилятор для приложения A и приложения B.

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

msg.length = htons(sizeof(ORIENTATION_MESSAGE) - sizeof(MESSAGE_HEADER));

, но вы не показываете код, который устанавливает сообщение ...

...