当前位置: 首页 > ops >正文

C++ 网络编程(9)字节序处理和消息队列的控制

文章目录

  • 前言
  • 字节序的问题
  • 一、目前服务器单线程操作流程
  • 二、大小端模式
  • 三、如何分辨本机字节序是大端序还是小端序
    • 代码
    • 细节详解
  • 四、服务器使用网络字节序
  • 五、消息队列控制
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

字节序的问题

在计算机网络中,由于不同的计算机使用的 CPU 架构和字节顺序可能不同
因此在传输数据时需要对数据的字节序进行统一,以保证数据能够正常传输和解析
这就是网络字节序的作用。

具体来说,计算机内部存储数据的方式有两种:大端序(Big-Endian)和小端序(Little-Endian)。在

大端序中,高位字节存储在低地址处,而低位字节存储在高地址处;在
小端序中,高位字节存储在高地址处,而低位字节存储在低地址处

网络通信过程中,通常使用的是大端序。这是因为早期的网络硬件大多采用了 Motorola 处理器,而 Motorola 处理器使用的是大端序。此外,大多数网络协议规定了网络字节序必须为大端序

因此,在进行网络编程时,需要将主机字节序转换为网络字节序,也就是将数据从本地字节序转换为大端序。可以使用诸如(本地转网络) htonlhtons、(网络转本地)ntohlntohs 等函数来实现字节序转换操作。

综上所述,网络字节序的主要作用是统一不同计算机间的数据表示方式,以保证数据在网络中的正确传输和解析


提示:以下是本篇文章正文内容,下面案例可供参考

一、目前服务器单线程操作流程

当应用层调用一个读操作或者写操作,这个时候会将读写操作和读写操作的回调函数注册进入io_context中然后注册给对应的模型
linux环境下会使用epoll模型windows环境下会使用iocp模型
它们是高效的操作系统底层事件通知机制
● 用来监控大量文件描述符(socket)上的事件(比如有没有数据可读、可写、连接是否断开等)。
● 解决了“多路复用”的问题——也就是让一个线程高效地管理成百上千的网络连接
epoll 和 IOCP 本身不会执行你的回调,而是当某些事件确定触发以后,其回调函数就会一个个的放入就绪事件队列中按顺序触发
在这里插入图片描述

二、大小端模式

当我们想存 0x12345678

大端模式
在这里插入图片描述
小端模式
在这里插入图片描述

三、如何分辨本机字节序是大端序还是小端序

如何区分本机字节序,可以通过判断低地址存储的数据是否为低字节数据,如果是则为小端,否则为大端,下面写一段代码讲述这个逻辑

代码

#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 = 0; i < sizeof(num); i++) {cout << hex << (int)*(p + i) << " ";}cout << endl;}return 0;
}

细节详解

if ((char)&num == 1)
这里我们在前面已经定义了num==1
在内存中,int 类型通常占 4 个字节
数值 1 在十六进制表示中是 0x00000001
然后我们判断最低位 是否为1就行
如果最低位为1 说明就是小端序 因为低位字节序存储在低地址

hex 是一个 格式控制符,它的作用是让 cout 以 十六进制 的格式输出后面的数值。

例如,如果当前系统为大端序,则输出结果为:

原始数据:12345678
当前系统为大端序
字节序为:12 34 56 78

如果当前系统为小端序,则输出结果为:

原始数据:12345678
当前系统为小端序
字节序为:78 56 34 12

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

为保证字节序一致性,网络传输使用网络字节序,也就是大端模式
boost::asio 库中,可以使用 boost::asio::detail::socket_ops::host_to_network_long()boost::asio::detail::socket_ops::host_to_network_short() 函数
将主机字节序转换为网络字节序。具体方法如下

#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;
}

上述代码中,使用了 boost::asio::detail::socket_ops::host_to_network_long()boost::asio::detail::socket_ops::host_to_network_short() 函数
将主机字节序转换为网络字节序

host_to_network_long() 函数将一个 32 位无符号整数从主机字节序转换为网络字节序,返回转换后的结果。host_to_network_short() 函数将一个 16 位无符号整数从主机字节序转换为网络字节序,返回转换后的结果。

在上述代码中,分别将 32 位和 16 位的主机字节序数值转换为网络字节序,并输出转换结果。需要注意的是,在使用这些函数时,应该确保输入参数和返回结果都是无符号整数类型,否则可能会出现错误

同样的道理,我们只需要在服务器发送数据时,将数据长度转化为网络字节序,在接收数据时,将长度转为本机字节序

//获取头部数据进行判断
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;

在服务器的发送数据时会构造消息节点,构造消息节点时,将发送长度由本地字节序转化为网络字节序

MsgNode(char* msg,short max_len):_cur_len(0),_total_len(HEAD_LENGTH+max_len)
{_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';
}

五、消息队列控制

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

void CSession::Send(char* msg, int max_length)
{std::lock_guard<std::mutex>lock (_send_lock);if (_send_que.size() > 1000) {cout << "session: " << _uuid << " send que fulled, size is 1000"<< endl;return;}_send_que.push(make_shared<MsgNode>(msg, max_length));if (_send_que.size() > 0)// 保证同一时刻只有一个 async_write 在进行,防止多次并发写{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, std::placeholders::_2, shared_from_this()));
}

加锁(std::lock_guardstd::mutex lock(_send_lock))
作用:保证同一时刻只有一个线程能进入这段 Send 代码,防止多线程下队列(_send_que)被并发操作而发生数据竞争或破坏

if (_send_que.size() > 0) return;
作用:保证同一时刻只有一个 async_write 正在进行
如果队列原来就有消息,说明之前已经有 async_write 在发送了,现在只需要把新消息放进队列即可,不需要再发起新的 async_write。
只有队列原来为空时,才会发起一次 async_write。

总结(更口语化):
锁:只让一个线程能“进屋”操作队列,避免线程打架
if 判断:只让“屋里”同一时刻只挂着一个 async_write,保证异步写不会重复发起

总结

本文介绍了如何使用网络字节序以及为什么使用网络字节序作为网络传输,且为发送队列设置了阈值保证发送数据的高效性

协议头部里的字段(比如长度、类型、端口号等)是数值型,需要进行主机字节序到网络字节序的转换,确保不同平台间通信时数值被一致解释。
协议内容/数据部分通常被当作原始字节流(无论是字符串还是二进制),直接按字节传输,不做字节序转换。

http://www.xdnf.cn/news/7625.html

相关文章:

  • 缺乏进度跟踪机制,如何掌握项目状态?
  • MyBatis常用方法
  • 零售EDI:Belk Stores EDI需求分析
  • 阅读笔记---城市计算中用于预测学习的时空图神经网络研究综述
  • 《从零开始构建高可用MySQL架构:全流程实战指南》
  • 无人机避障——深蓝学院浙大Fast-planner学习部分(轨迹生成B-Spline部分)
  • Spring是如何实现scope作用域支持
  • 家用和类似用途电器的安全 第1部分:通用要求 与2005版差异(6)
  • pmap中的mode列,脏页,写时复制
  • 公路水运安全员C证用途及重要性
  • 测试工程师要如何开展单元测试
  • JavaSenderMail发送邮件(QQ及OFFICE365)
  • 如何使用通义灵码玩转Python - AI编程助手提升效率
  • 【工具变量】地级市健康城市试点政策数据集(2007-2024年)
  • 香港科技大学广州香港科技大学硕博士研究生学位项目宣讲会(智能制造硕博士物理学硕士)—深圳大学专场
  • 大模型从基础到入门 记录
  • 测试W5500的第3步_使用ioLibrary库创建TCPServer
  • [特殊字符] jQuery 响应式瀑布流布局插件推荐!
  • 2025年JIII SCI1区TOP,多策略霜冰优化算法IRIME+无人机路径规划,深度解析+性能实测
  • [创业之路-370]:企业战略管理案例分析-10-战略制定-差距分析的案例之小米
  • AI大模型从0到1记录学习 大模型技术之数学基础 day26
  • AR0144CSSC20SUKA0-CRBR——1/4英寸 1.0 MP 高性能CMOS图像传感器解析
  • 多路视频直播用在线云导播切换的效果测试
  • [春秋云镜] Spoofing仿真场景
  • 软考软件测评师——系统安全设计(防火墙技术)
  • 每日一题:1、虚拟IPv4地址转换为32位整数(JS)
  • 你知道mysql的索引下推么?
  • 【办公类-18-04】(Python)“验血单信息”批量生成打印(学校、班级、姓名、性别)
  • 三色光源投影暗战:FSHD 如何撕开 DLP/3LCD 垄断缺口?
  • Ubuntu软件仓库与更新源配置指南