通过 TCP 发送 std::vector<float>,从 boost::asio 到 QTcpSocket
Send std::vector<float> over TCP, from boost::asio to QTcpSocket
服务器需要通过 TCP 套接字将 std::vector<float>
发送到 Qt
应用程序。我正在使用 Qt 5.7。
在服务器端,使用 boost::asio
:
std::vector<float> message_ = {1.2, 8.5};
asio::async_write(socket_, asio::buffer<float>(message_),
[this, self](std::error_code ec, std::size_t)
这行得通,我设法使用 boost::asio
的 read_some()
将其恢复到我的客户端。由于 Qt
和 asio
都有自己的事件管理器,我想避免在我的 Qt
应用程序中使用 asio
。
所以在客户端我有(这不起作用):
client.h:
#define FLOATSIZE 4
QTcpSocket *m_socket;
QDataStream m_in;
QString *m_string;
QByteArray m_buff;
client.cpp(构造函数):
m_in.setDevice(m_socket);
m_in.setFloatingPointPrecision(QDataStream::SinglePrecision);
// m_in.setByteOrder(QDataStream::LittleEndian);
client.cpp(读取函数,通过QObject::connect(m_socket, &QIODevice::readyRead, this, &mywidget::ask2read);
连接):
uint availbytes = m_socket->bytesAvailable(); // which is 8, so that seems good
while (availbytes >= FLOATSIZE)
{
nbytes = m_in.readRawData(m_buff.data(), FLOATSIZE);
bool conv_ok = false;
const float f = m_buff.toFloat(&conv_ok);
availbytes = m_socket->bytesAvailable();
m_buff.clear();
}
m_buff.toFloat()
调用 returns 0.0
根据 Qt 文档,这是失败的。我试图改变浮点精度,小端或大端,但我无法设法让我的 std::vector<float>
回来。有什么提示吗?
编辑:一切都在相同的 PC/compiler.
上运行
编辑:查看我的解决方案答案和 sehe's 了解更多详细信息
我认为使用高级发送方法服务器端(您尝试发送 C++ 向量)和低级客户端是个坏主意。
我很确定某处存在字节顺序问题。
无论如何尝试做这个客户端:
char buffer[FLOATSIZE];
bytes = m_in.readRawData(buffer, FLOATSIZE);
if (bytes != FLOATSIZE)
return ERROR;
const float f = (float)(ntohl(*((int32_t *)buffer)));
如果 boost::asio 对浮点数使用网络字节顺序(它应该如此),这将起作用。
我设法通过编辑 Qt
端(客户端)来读取套接字来解决问题:
uint availbytes = m_socket->bytesAvailable();
while (availbytes >= 4)
{
char buffer[FLOATSIZE];
nbytes = m_in.readRawData(buffer, FLOATSIZE);
float f = bytes2float(buffer);
availbytes = m_socket->bytesAvailable();
}
我使用这两个转换函数,bytes2float
和 bytes2int
:
float bytes2float(char* buffer)
{
union {
float f;
uchar b[4];
} u;
u.b[3] = buffer[3];
u.b[2] = buffer[2];
u.b[1] = buffer[1];
u.b[0] = buffer[0];
return u.f;
}
和:
int bytes2int(char* buffer)
{
int a = int((unsigned char)(buffer[3]) << 24 |
(unsigned char)(buffer[2]) << 16 |
(unsigned char)(buffer[1]) << 8 |
(unsigned char)(buffer[0]));
return a;
}
我还发现了显示字节的函数,这对于查看幕后发生的事情很有用(来自):
template <typename T>
void print_bytes(const T& input, std::ostream& os = std::cout)
{
const unsigned char* p = reinterpret_cast<const unsigned char*>(&input);
os << std::hex << std::showbase;
os << "[";
for (unsigned int i=0; i<sizeof(T); ++i)
os << static_cast<int>(*(p++)) << " ";
os << "]" << std::endl;;
}
回复。 : 这是哪一边?另外,你们的平台不一样吗(OS/architecture?)。我从问题中假设同一台 PC 和编译器等上的两个进程 运行
一方面,您可以看到 ASIO does not do anything related to endianness。
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace asio = boost::asio;
#include <iostream>
void print_bytes(unsigned char const* b, unsigned char const* e)
{
std::cout << std::hex << std::setfill('0') << "[ ";
while (b!=e)
std::cout << std::setw(2) << static_cast<int>(*b++) << " ";
std::cout << "]\n";
}
template <typename T> void print_bytes(const T& input) {
using namespace std;
print_bytes(reinterpret_cast<unsigned char const*>(std::addressof(*begin(input))),
reinterpret_cast<unsigned char const*>(std::addressof(*end(input))));
}
int main() {
float const fs[] { 1.2, 8.5 };
std::cout << "fs: "; print_bytes(fs);
{
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(fs));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
{
std::vector<char> binary(2*sizeof(float));
asio::buffer_copy(asio::buffer(binary), asio::buffer(fs));
std::cout << "binary: "; print_bytes(binary);
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(binary));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
}
版画
fs: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
binary: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
理论
我怀疑 Qt 端破坏了东西。由于函数 readRawData
的命名肯定意味着缺乏字节序意识,我猜你的系统的字节顺序造成了严重破坏(,也是评论)。
建议
在这种情况下,请考虑使用 Boost Endian。
服务器需要通过 TCP 套接字将 std::vector<float>
发送到 Qt
应用程序。我正在使用 Qt 5.7。
在服务器端,使用 boost::asio
:
std::vector<float> message_ = {1.2, 8.5};
asio::async_write(socket_, asio::buffer<float>(message_),
[this, self](std::error_code ec, std::size_t)
这行得通,我设法使用 boost::asio
的 read_some()
将其恢复到我的客户端。由于 Qt
和 asio
都有自己的事件管理器,我想避免在我的 Qt
应用程序中使用 asio
。
所以在客户端我有(这不起作用):
client.h:
#define FLOATSIZE 4
QTcpSocket *m_socket;
QDataStream m_in;
QString *m_string;
QByteArray m_buff;
client.cpp(构造函数):
m_in.setDevice(m_socket);
m_in.setFloatingPointPrecision(QDataStream::SinglePrecision);
// m_in.setByteOrder(QDataStream::LittleEndian);
client.cpp(读取函数,通过QObject::connect(m_socket, &QIODevice::readyRead, this, &mywidget::ask2read);
连接):
uint availbytes = m_socket->bytesAvailable(); // which is 8, so that seems good
while (availbytes >= FLOATSIZE)
{
nbytes = m_in.readRawData(m_buff.data(), FLOATSIZE);
bool conv_ok = false;
const float f = m_buff.toFloat(&conv_ok);
availbytes = m_socket->bytesAvailable();
m_buff.clear();
}
m_buff.toFloat()
调用 returns 0.0
根据 Qt 文档,这是失败的。我试图改变浮点精度,小端或大端,但我无法设法让我的 std::vector<float>
回来。有什么提示吗?
编辑:一切都在相同的 PC/compiler.
上运行编辑:查看我的解决方案答案和 sehe's 了解更多详细信息
我认为使用高级发送方法服务器端(您尝试发送 C++ 向量)和低级客户端是个坏主意。
我很确定某处存在字节顺序问题。
无论如何尝试做这个客户端:
char buffer[FLOATSIZE];
bytes = m_in.readRawData(buffer, FLOATSIZE);
if (bytes != FLOATSIZE)
return ERROR;
const float f = (float)(ntohl(*((int32_t *)buffer)));
如果 boost::asio 对浮点数使用网络字节顺序(它应该如此),这将起作用。
我设法通过编辑 Qt
端(客户端)来读取套接字来解决问题:
uint availbytes = m_socket->bytesAvailable();
while (availbytes >= 4)
{
char buffer[FLOATSIZE];
nbytes = m_in.readRawData(buffer, FLOATSIZE);
float f = bytes2float(buffer);
availbytes = m_socket->bytesAvailable();
}
我使用这两个转换函数,bytes2float
和 bytes2int
:
float bytes2float(char* buffer)
{
union {
float f;
uchar b[4];
} u;
u.b[3] = buffer[3];
u.b[2] = buffer[2];
u.b[1] = buffer[1];
u.b[0] = buffer[0];
return u.f;
}
和:
int bytes2int(char* buffer)
{
int a = int((unsigned char)(buffer[3]) << 24 |
(unsigned char)(buffer[2]) << 16 |
(unsigned char)(buffer[1]) << 8 |
(unsigned char)(buffer[0]));
return a;
}
我还发现了显示字节的函数,这对于查看幕后发生的事情很有用(来自):
template <typename T>
void print_bytes(const T& input, std::ostream& os = std::cout)
{
const unsigned char* p = reinterpret_cast<const unsigned char*>(&input);
os << std::hex << std::showbase;
os << "[";
for (unsigned int i=0; i<sizeof(T); ++i)
os << static_cast<int>(*(p++)) << " ";
os << "]" << std::endl;;
}
回复。
一方面,您可以看到 ASIO does not do anything related to endianness。
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
namespace asio = boost::asio;
#include <iostream>
void print_bytes(unsigned char const* b, unsigned char const* e)
{
std::cout << std::hex << std::setfill('0') << "[ ";
while (b!=e)
std::cout << std::setw(2) << static_cast<int>(*b++) << " ";
std::cout << "]\n";
}
template <typename T> void print_bytes(const T& input) {
using namespace std;
print_bytes(reinterpret_cast<unsigned char const*>(std::addressof(*begin(input))),
reinterpret_cast<unsigned char const*>(std::addressof(*end(input))));
}
int main() {
float const fs[] { 1.2, 8.5 };
std::cout << "fs: "; print_bytes(fs);
{
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(fs));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
{
std::vector<char> binary(2*sizeof(float));
asio::buffer_copy(asio::buffer(binary), asio::buffer(fs));
std::cout << "binary: "; print_bytes(binary);
std::vector<float> gs(2);
asio::buffer_copy(asio::buffer(gs), asio::buffer(binary));
for (auto g : gs) std::cout << g << " "; std::cout << "\n";
std::cout << "gs: "; print_bytes(gs);
}
}
版画
fs: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
binary: [ 9a 99 99 3f 00 00 08 41 ]
1.2 8.5
gs: [ 9a 99 99 3f 00 00 08 41 ]
理论
我怀疑 Qt 端破坏了东西。由于函数 readRawData
的命名肯定意味着缺乏字节序意识,我猜你的系统的字节顺序造成了严重破坏(,也是评论)。
建议
在这种情况下,请考虑使用 Boost Endian。