我试图从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之间转换的问题,但我似乎无法弄明白。非常感谢任何帮助。
答案 0 :(得分:1)
buffer
正在使用char
,可能已在您的系统上签名。
如果buffer[0]
为-1
,那么它将-1
转换为int
(不再是char
)。将所有这些字节一起添加时会导致问题(因为-1
可能char
可能0xff
,而int
可能0xffffffff
float
)。换句话说,如果缓冲区包含具有负值的字节,那么就会搞砸&#34;合并&#34;由整数提升引起的字节数。
您正在通过重新解释投射来破坏严格的别名规则。这很可能适用于您可能使用的任何系统,但严格来说,这是一种未定义的行为。
您可以使用直接将字节分配给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 ++应用程序相同的计算机上运行,请将>
更改为@
或=
。