从tcp读取浮点数

时间:2015-06-29 03:13:30

标签: c++ tcp type-conversion

我试图从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之间转换的问题,但我似乎无法弄明白。非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

  1. buffer正在使用char,可能已在您的系统上签名。

    如果buffer[0]-1,那么它将-1转换为int(不再是char)。将所有这些字节一起添加时会导致问题(因为-1可能char可能0xff,而int可能0xffffffff float )。换句话说,如果缓冲区包含具有负值的字节,那么就会搞砸&#34;合并&#34;由整数提升引起的字节数。

  2. 您正在通过重新解释投射来破坏严格的别名规则。这很可能适用于您可能使用的任何系统,但严格来说,这是一种未定义的行为。

  3. 您可以使用直接将字节分配给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); 完成所有这些:

    <a href="LOCATION-OF-MY-FOLDER"</a>

答案 1 :(得分:0)

您的代码正在使用char来进行小提示,这可能是默认情况下char已签名的平台上的问题(大多数都是这样)。使用unsigned char更适合这种用途。

此外,您没有正确地实现从套接字读取。 recv来电无法保证获得您要求的数据量...换句话说,传递的大小只是最大数量,而不是可以读取和有效收到的数据可能会少一些。

从套接字读取时,需要进行循环,在接收到所有数据或结果为负或为零时退出(否定意味着错误,零意味着另一端关闭了端点,没有更多数据是来)。

答案 2 :(得分: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>

另一种选择不是给自己造成这种痛苦。从某种意义上说,这是你自己在python脚本中使用packer = struct.Struct('>%sf' % 8)所做的一切。您显然知道目标将在小端机器上运行,因此将>构造函数中的Struct更改为&#39;&lt;&#39;。如果您知道python脚本将在与C ++应用程序相同的计算机上运行,​​请将>更改为@=