C++抽象是Endian中立的吗?

C++抽象是Endian中立的吗?,c++,endianness,modbus,modbus-tcp,C++,Endianness,Modbus,Modbus Tcp,假设我有一个客户机和一个服务器,它们通过一些网络协议(比如ModbusTCP)相互通信16位数字,但协议在这里并不相关 现在我知道,客户端的字节数是我的PC机和服务器端的大的一些PLC,客户端用Boost Asio套接字完全写在C++中。在这个设置中,我认为必须交换从服务器接收的字节,以便将数字正确存储在uint16_t变量中,但是这是错误的,因为我读取的值不正确 我的理解是,我的C++抽象把正确的值存储到变量中,而不需要我真正关心交换或循环。考虑这个片段: // received 0x0201

假设我有一个客户机和一个服务器,它们通过一些网络协议(比如ModbusTCP)相互通信16位数字,但协议在这里并不相关

现在我知道,客户端的字节数是我的PC机和服务器端的大的一些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
这让我有些惊讶,因为如果我查看带有指针的内存表示,我发现它们实际上存储在客户端的little endian中:

// 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
因此,问题是:

<> P>只要我不占用内存地址和指针,C++就可以保证我从网络中读取的值无论服务器的端部是否正确,正确吗?或者我在这里遗漏了什么

编辑: 感谢您的回答,我想补充一点,我目前正在使用boost::asio::writesocket、boost::asio::bufferdata将数据从客户端发送到服务器,数据是std::vector。所以我的理解是,只要我按照网络顺序填充数据,我就不应该关心我的系统,甚至16位数据的服务器端,因为我是在操作值,而不是直接从内存读取字节,对吗


要使用htons系列函数,我必须将底层TCP层更改为使用memcpy或类似的功能以及uint8_t*数据缓冲区,这更像是C风格而不是C++风格,我为什么要这样做?有没有我没有看到的优势?

static\u casthhighstatic\u casthhigh您的代码的第一个片段工作正常,因为您不直接使用字节地址。由于运算符的定义,这些代码的编译结果与您的平台端性无关。代码的第一个片段工作正常,因为您不直接使用字节地址。由于操作员对Modbus的定义,,编译该代码以获得正确的操作结果,与您的平台端性无关:

对于16位字,Modbus首先发送最高有效字节,这意味着它使用Big-Endian,然后如果客户端或服务器使用Little-Endian,则在发送或接收时必须交换字节

另一个问题是Modbus没有定义32位类型的16位寄存器的发送顺序

Modbus服务器设备首先发送最重要的16位寄存器,其他设备则相反。为此,唯一的解决方案是在客户端配置中交换16位寄存器的可能性

传输字符串时也会出现类似问题,一些服务器不发送abcdef,而是发送badcfe

关于Modbus:

对于16位字,Modbus首先发送最高有效字节,这意味着它使用Big-Endian,然后如果客户端或服务器使用Little-Endian,则在发送或接收时必须交换字节

另一个问题是Modbus没有定义32位类型的16位寄存器的发送顺序

Modbus服务器设备首先发送最重要的16位寄存器,其他设备则相反。为此,唯一的解决方案是在客户端配置中交换16位寄存器的可能性


传输字符串时也会出现类似问题,有些服务器不发送abcdef send badcfe

C++可以保证从网络读取的值是正确的,无论服务器的尾端是什么,正确:否,要在服务器和客户端中使用相同的源定义,并确保以正确的顺序读取integer,一种方法是在写入和读取时始终使用ntohl和reverse之类的函数来要求他们执行此工作,因为他们知道你是否在little/big-endian如果你要在little-endian机器上序列化一个对象,按字节顺序排列,然后在big-endian机器上反序列化该网络传输,那么顺序绝对重要,假设对齐和填充没有首先妨碍。这就是为什么我们不这样序列化,这就是为什么在编写通信协议时需要定义网络字节顺序。您的通信协议Boost和/或Modbus很可能为您处理这个问题。C++可以保证我从网络中读取的值无论服务器的EnDon是否正确,正确:不,要在服务器和客户端中使用相同的源定义,并确保以正确的顺序读取整数,一种方法是在wri时始终使用诸如ntohl和reverse之类的函数
te和read让他们做这项工作,因为他们知道你是否在little/big-endian中如果你要在little-endian机器上按字节顺序序列化一个对象,然后在big-endian机器上反序列化该网络传输,顺序绝对重要,前提是对齐和填充没有首先妨碍。这就是为什么我们不这样序列化,这就是为什么在编写通信协议时需要定义网络字节顺序。您的通信协议boost和/或modbus可能会为您解决这个问题。我明白了,但我根本不使用memcpy,我使用boost::asio::writesocket,boost::asio::bufferdata;,其中数据是std::vector。所以,只要我按网络顺序填充数据,我就不应该使用HTON,对吗?我明白了,但我根本不使用memcpy,我使用boost::asio::writesocket,boost::asio::bufferdata;,其中数据是std::vector。所以,只要我按照网络顺序填充数据,我就不应该使用HTON,对吗?对,对于32位数据,我可能需要执行一些字交换来实现BADC或CDAB,谢谢。对32位数据,我可能需要执行一些字交换来实现BADC或CDAB,谢谢
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
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