簡體   English   中英

從TCP讀取浮點數

[英]Read floats from tcp

我正在嘗試讀取從Python tcp流服務器中的打包結構傳遞的big-endian 8 32位浮點數。 似乎接近工作了,但是前幾個值相差一個奇數,而后面的值相差很小,甚至是不精確。

例如,以下是客戶端解釋的值:

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和float之間轉換的問題,但我似乎無法弄清楚。 任何幫助是極大的贊賞。

  1. buffer正在使用char ,這很可能在您的系統上簽名。

    如果buffer[0]-1 ,那么它將作為int轉換為-1 (不再是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);

您的代碼使用char s進行位擺弄,這在默認情況下對char是簽名的平台(大多數都是)上可能是個問題。 為此,使用unsigned char更好。

此外,您沒有正確實現從套接字讀取。 recv調用不能保證獲得您所請求的數據量...換句話說,傳遞的大小只是最大數量,無法讀取,有效接收的數據可能更少。

從套接字讀取數據時,您需要進行循環,在接收到所有數據或結果為負數或零(負數表示錯誤,零表示另一端已關閉端點且不再有數據)時退出。

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

這不是字節交換32位整數的正確方法。 如果char被簽名怎么辦? (如果您使用的是Windows,Linux或OSX,就是這種情況。)

正確的方法是使用function / macro ntohl 盡管這不是C或C ++標准的一部分,但是您會在希望通過Internet正常工作的任何小端系統上找到它。 在Unix機器上,您需要#include <arpa/inet.h> 在Windows上,您需要#include <winsock2.h>

另一種選擇是不要對自己造成這種痛苦。 從某種意義上講,這是您自己做的, packer = struct.Struct('>%sf' % 8)在python腳本中使用packer = struct.Struct('>%sf' % 8) 您顯然知道目標將在小型Endian機器上運行,因此將Struct構造函數中的>更改為'<'。 如果您知道python腳本將與C ++應用程序在同一台計算機上運行,​​請將>更改為@=

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM