字节序大小端问题

字节序的问题

字节序问题,通常被称为大小端问题(Endianess),涉及到数据在计算机内存中的存储方式,尤其是多字节值(如整数、浮点数)的存储顺序。这个问题在不同架构的计算机系统中尤为重要,因为它会影响数据的解释和处理。主要有两种类型的字节序:

  1. 大端序(Big Endian):在这种方式下,多字节值的最高有效字节(MSB)存储在最低的内存地址,随后是次高有效字节,以此类推。例如,数值 0x12345678 在内存中会被存储为 12 34 56 78(地址由低到高排列)。
  2. 小端序(Little Endian):与大端序相反,小端序将多字节值的最低有效字节(LSB)存储在最低的内存地址。同样的数值 0x12345678 在小端序系统中将被存储为 78 56 34 12

字节序问题在跨平台数据交换时尤为重要。如果两个交流数据的系统采用不同的字节序,没有适当的转换,数据可能会被错误地解释,导致问题。例如,在网络编程中,通常采用大端序(网络字节序),因此在数据发送和接收时,可能需要在小端序和大端序之间进行转换。

产生问题的原因

在网络通信中,如果数字(如整数或浮点数)的字节序没有从发送方的主机序转换为统一的网络字节序(大端序),那么接收方可能无法正确解释接收到的数据。这是因为不同的计算机架构可能采用不同的字节序,导致相同的数字在不同系统上的内存表示不同

如何区分本机字节序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
// 判断当前系统的字节序是大端序还是小端序
bool is_big_endian() {
int num = 1;
if (*(char*)&num == 1) {
// 当前系统为小端序
return false;
} else {
// 当前系统为大端序
return true;
}
}
int main() {
int num = 0x12345678;
char* p = (char*)&num;
cout << "原始数据:" << hex << num << endl;
if (is_big_endian()) {
cout << "当前系统为大端序" << endl;
cout << "字节序为:";
for (int i = 0; i < sizeof(num); i++) {
cout << hex << (int)*(p + i) << " ";
}
cout << endl;
} else {
cout << "当前系统为小端序" << endl;
cout << "字节序为:";
for (int i = sizeof(num) - 1; i >= 0; i--) {
cout << hex << (int)*(p + i) << " ";
}
cout << endl;
}
return 0;
}

服务器使用网络字节序

为保证字节序一致性,网络传输使用网络字节序,也就是大端模式。

可以使用
boost::asio::detail::socket_ops::host_to_network_long()boost::asio::detail::socket_ops::host_to_network_short() 函数

将主机字节序转换为网络字节序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <boost/asio.hpp>
#include <iostream>
int main()
{
uint32_t host_long_value = 0x12345678;
uint16_t host_short_value = 0x5678;
uint32_t network_long_value = boost::asio::detail::socket_ops::host_to_network_long(host_long_value);
uint16_t network_short_value = boost::asio::detail::socket_ops::host_to_network_short(host_short_value);
std::cout << "Host long value: 0x" << std::hex << host_long_value << std::endl;
std::cout << "Network long value: 0x" << std::hex << network_long_value << std::endl;
std::cout << "Host short value: 0x" << std::hex << host_short_value << std::endl;
std::cout << "Network short value: 0x" << std::hex << network_short_value << std::endl;
return 0;
}

输出结果:

1
2
3
4
Host long value: 0x12345678
Network long value: 0x78563412
Host short value: 0x5678
Network short value: 0x7856

注意:在使用这些函数时,应该确保输入参数和返回结果都是无符号整数类型,否则可能会出现错误。

服务器发送数据时,将数据长度转化为网络字节序
接收数据时,将长度转为本机字节序

将网络字节序转换为主机字节序

1
2
3
4
5
short data_len = 0;
memcpy(&data_len, _recv_head_node->_data, HEAD_LENGTH);
//网络字节序转化为本地字节序
data_len=boost::asio::detail::socket_ops::network_to_host_short(data_len);
cout << "data_len is " << data_len << endl;

消息队列控制

发送时我们会将发送的消息放入队列里以保证发送的时序性,每个session都有一个发送队列,因为有的时候发送的频率过高会导致队列增大,所以要对队列的大小做限制,当队列大于指定数量的长度时,就丢弃要发送的数据包,以保证消息的快速收发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void CSession::Send(char* msg, int max_length) {
std::lock_guard<std::mutex> lock(_send_lock);
int send_que_size = _send_que.size();

//如果大于"队列size允许的容量",则不允许发送
if (send_que_size > MAX_SENDQUE) {
cout << "session: " << _uuid << " send que fulled, size is " << MAX_SENDQUE << endl;
return;
}
_send_que.push(make_shared<MsgNode>(msg, max_length));
if (send_que_size>0) {
return;
}
auto& msgnode = _send_que.front();
boost::asio::async_write(_socket, boost::asio::buffer(msgnode->_data, msgnode->_total_len),
std::bind(&CSession::HandleWrite, this, std::placeholders::_1, SharedSelf()));
}

总结

字节序问题的解决需要程序员显示的调用函数,asio不会自动帮我们实现


字节序大小端问题
http://example.com/2023/11/16/cpp/字节序大小端问题/
作者
Mrxiad
发布于
2023年11月16日
许可协议