字节序的问题
字节序问题,通常被称为大小端问题(Endianess),涉及到数据在计算机内存中的存储方式,尤其是多字节值(如整数、浮点数)的存储顺序。这个问题在不同架构的计算机系统中尤为重要,因为它会影响数据的解释和处理。主要有两种类型的字节序:
- 大端序(Big Endian):在这种方式下,多字节值的最高有效字节(MSB)存储在最低的内存地址,随后是次高有效字节,以此类推。例如,数值
0x12345678 在内存中会被存储为 12 34 56 78(地址由低到高排列)。
- 小端序(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*)# 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(); 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不会自动帮我们实现