九、字节序处理和消息队列控制

        系列文章目录:C++ asio网络编程-CSDN博客 

1、字节序的问题

        先引用网上的统一写法:计算机内部存储数据的方式有两种:大端序(Big-Endian)和小端序(Little-Endian)。在大端序中,高位字节存储在低地址处,而低位字节存储在高地址处;在小端序中,高位字节存储在高地址处,而低位字节存储在低地址处。

        开始讲人话:我们都知道,int占四个字节的内存,假设现在有一个变量:

int num = 0x12345678;

        那么它将被拆为四个字节存储:12、34、56、78,假设存储他们的内存地址为0x00、0x01、0x02、0x03,那么大端模式的存储方式为:

        小端模式的存储方式为:

        这样再带入天书中的 “大端序中,高位字节存储在低地址处,而低位字节存储在高地址处;在小端序中,高位字节存储在高地址处,而低位字节存储在低地址处。” 应该就很清楚了。

2、判断本机字节序

        其实原理很简单,根据上面的解释,我们可以设置一个变量

int num = 1;

        那么它在内存中的存储应该被分为:00 00 00 01 这四份,然后我们将他的指针转化为char*类型,再去解引用看看是不是1

if (*(char*)&num == 1) {
        // 当前系统为小端序
        return false;
    } else {
        // 当前系统为大端序
        return true;
    }
}

        首先通过&num得到他的地址,(char*)将他转化为char*类型,再去解引用得到它的值。如果是大端,就是00 00 00 01依次存储,转化为char一个字节前面的舍去,留下01,所以就返回true,如果是小段,就是01 00 00 00依次存储,转化为char就只保留最后一个00,返回false。通过这段代码就很容易判断本机字节序。

3、服务器使用网络字节序

        网络字节序其实就是大端模式,因为大多数网络协议规定了网络字节序必须为大端序。在boost::asio中也提供了api供我们使用:

  • boost::asio::detail::socket_ops::host_to_network_short

    uint16_t 类型的值从主机字节序转换为网络字节序(大端字节序)。
  • boost::asio::detail::socket_ops::host_to_network_long

    uint32_t 类型的值从主机字节序转换为网络字节序(大端字节序)。
  • boost::asio::detail::socket_ops::network_to_host_short

    uint16_t 类型的值从网络字节序(大端字节序)转换为主机字节序。
  • boost::asio::detail::socket_ops::network_to_host_long

    uint32_t 类型的值从网络字节序(大端字节序)转换为主机字节序。

        需要注意的是,在使用这些函数时,应该确保输入参数和返回结果都是无符号整数类型,否则可能会出现错误。 同样的道理,我们只需要在服务器发送数据时,将数据长度转化为网络字节序,在接收数据时,将长度转为本机字节序。 在服务器的HandleRead函数里,添加对data_len的转换,将网络字节转为本地字节序:

// 获取数据长度
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);
std::cout << "data len: " << data_len << std::endl;
// 头部长度非法
if (data_len > MAX_LENGTH) {
				std::cout << "invalid data length" << std::endl;
				_server->clearSession(_uuid);
				return;
}
MsgNode(char* msg, int max_len) : _total_len(max_len + HEAD_LENGTH), _cur_len(0) {
	_data = new char[_total_len + 1]();
	// 本地字节序转化为网络字节序
	int max_len_host = boost::asio::detail::socket_ops::host_to_network_short(max_len);
	// 接收到的数据长度
	memcpy(_data, &max_len_host, HEAD_LENGTH);
	// 接收到的数据
	memcpy(_data + HEAD_LENGTH, msg, max_len);
	// 数据结束标志,其实是为了方便控制台打印用的
	_data[_total_len] = '\0';
}

        客户端的处理方式相同。

4、消息队列控制

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

#define MAX_SENDQUE 1000
void Session::send(char* msg, int max_length)
{
	bool pending = false;

	std::lock_guard<std::mutex> lock(_send_lock);

	if (_send_que.size() > MAX_SENDQUE) {
		std::cout << "session: " << _uuid << " send que fulled, size is " << MAX_SENDQUE << std::endl;
		return;
	}

	if (_send_que.size() > 0) {
		pending = true;
	}
	_send_que.push(std::make_shared<MsgNode>(msg, max_length));
	if (pending) {
		return;
	}

	auto& msgnode = _send_que.front();

	boost::asio::async_write(_socket, boost::asio::buffer(msgnode->_data, msgnode->_total_len),
		std::bind(&Session::handle_write, this, std::placeholders::_1, shared_from_this()));
}

5、总结

        本文介绍了网络字节序以及控制它的必要性,限制了发送队列长度保证发送数据的高效率。

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值