假设我有一个客户机和一个服务器,它们通过某些网络协议(例如ModbusTCP)相互通信16位数字,但是该协议在这里不相关。
现在我知道,客户端的字节序很小(我的电脑),而服务器的字节序很大。(在某些PLC上),客户端完全是使用Boost Asio套接字的C ++编写的。通过这种设置,我认为我必须交换从服务器接收的字节以将数字正确存储在uint16_t变量中,但是这是错误的,因为我正在读取不正确的值。
到目前为止,我的理解是,我的C ++抽象将值正确存储到变量中,而无需我真正关心交换或字节序。请考虑以下代码段:
// received 0x0201 (513 in big endian)
uint8_t high { 0x02 }; // first byte
uint8_t low { 0x01 }; // second byte
// merge into 16 bit value (no swap)
uint16_t val = (static_cast<uint16_t>(high)<< 8) | (static_cast<uint16_t>(low));
std::cout<<val; //correctly prints 513
这让我有些惊讶,也因为如果我用指针查看内存表示,我发现它们实际上存储在客户端的小字节序中:
// take the address of val, convert it to uint8_t pointer
auto addr = static_cast<uint8_t*>(&val);
// take the first and second bytes and print them
printf ("%d ", (int)addr[0]); // print 1
printf ("%d", (int)addr[1]); // print 2
所以问题是:
只要我不弄乱内存地址和指针,C ++就可以保证我从网络读取的值是正确的,无论服务器的字节序是正确的吗?还是我在这里想念什么?
编辑:
感谢您的回答,我想补充一下,我当前正在使用它boost::asio::write(socket, boost::asio::buffer(data))
来将数据从客户端发送到服务器,并且数据是std::vector<uint8_t>
。因此,我的理解是,只要我按网络顺序填充数据,就不必担心系统(甚至服务器的16位数据)的字节顺序,因为我是在“值”上操作,而不是直接读取字节从记忆中吧?
要使用htons
一系列功能,我必须更改要使用的基本TCP层memcpy
或类似的层以及一个uint8_t*
数据缓冲区,即更多的C风格而不是C ++ ish,为什么我要这样做?我没有看到优势吗?
(static_cast<uint16_t>(high)<< 8) | (static_cast<uint16_t>(low))
无论字节序如何,字节都具有相同的行为,数字的“左”端将始终是最高有效位,字节序仅会改变该位在第一个还是最后一个字节中。
例如:
uint16_t input = 0x0201;
uint8_t leftByte = input >> 8; // same result regardless of endianness
uint8_t rightByte = input & 0xFF; // same result regardless of endianness
uint8_t data[2];
memcpy(data, &input, sizeof(input)); // data will be {0x02, 0x01} or {0x01, 0x02} depending on endianness
另一个方向也是如此:
uint8_t data[] = {0x02, 0x01};
uint16_t output1;
memcpy(&output1, data, sizeof(output1)); // will be 0x0102 or 0x0201 depending on endianness
uint16_t output2 = data[1] << 8 | data[0]; // will be 0x0201 regardless of endianness
为确保您的代码在所有平台上都能正常使用,请最好使用htons
和ntohs
系列功能:
uint16_t input = 0x0201; // input is in host order
uint16_t networkInput = htons(input);
uint8_t data[2];
memcpy(data, &networkInput , sizeof(networkInput));
// data is big endian or "network" order
uint16_t networkOutput;
memcpy(&networkOutput, &data, sizeof(networkOutput));
uint16_t output = ntohs(networkOutput); // output is in host order
我知道了,但是我根本不使用memcpy,而是使用
boost::asio::write(socket, boost::asio::buffer(data));
data为的地方std::vector<uint8_t>
。因此,只要我按网络顺序填写数据,就不应该使用htons
,对吗?