2010-09-20 33 views
3

我有一個非常奇怪的錯誤。通過TCP/IP傳輸float值和數據損壞

我有兩個通過TCP/IP進行通信的應用程序。

應用程序A是服務器,應用程序B是客戶端。

應用程序A每隔100毫秒嚮應用程序B發送一堆浮點值。

該錯誤是:有時會出現一些由應用B接收的浮點值的不相同,通過應用A發送

最初的值,我認爲有與以太網或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'語句返回時被推入CPU的浮點堆棧中。調用者然後彈出堆棧中的值,該值被舍入,但它是交換的浮點數,因此舍入會混淆該值。

+1

你應該檢查些認同的答案關上你的一些其他問題。 – 2010-09-20 16:58:44

+1

請回頭接受你的一些問題的答案。這是堆棧溢出用來鼓勵人們回答額外問題的機制。 – atk 2010-09-20 16:59:07

+0

我該怎麼做?我沒有看到任何「接受」按鈕。 – axilmar 2010-09-20 21:36:33

回答

0

您通過網絡發送二進制數據,對結構佈局使用實現定義的填充,因此只有在應用程序A和應用程序B都使用相同的硬件,操作系統和編譯器時才能使用。

如果沒關係,但是我看不出你的代碼有什麼問題。一個潛在的問題是,您使用ntohs來提取消息的長度,並且該長度是總長度減去標題長度,所以您需要確保正確設置它。它需要做的

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

,但你不顯示,設置了消息的代碼...

+0

這不是填充。我使用#pragma pack(push,1),因此打包是1個字節。如果是填充,問題會立即顯現。 – axilmar 2010-09-20 21:37:59

3

TCP會嘗試提供未更改的字節,但除非機器具有類似的CPU和操作系統,否則不能保證一個系統上的浮點表示與另一個系統上的浮點表示相同。您需要一種機制來確保這一點,例如XDR或Google的protobuf。

+0

是的,這些機器具有類似的CPU和操作系統,並且這兩個程序都使用相同的代碼庫和編譯器。 – axilmar 2010-09-20 21:37:21