Boost ASIO 库深入学习(3)
Boost ASIO 库深入学习(3)
UDP简单通信导论
在继续深入前,我们不妨也来点碎碎念,因为UDP通信协议的模型与TCP是不同的,这种差异正是理解“无连接通信”的关键所在。我们下面要构建的,是一个经典的UDP通信对,UDP服务器(接收特定的消息)与UDP客户端(发送数据报文并等待可能的响应)。这将帮助我们在编程过程中更加明确目标。
UDP通信是轻量级的。UDP客户端若想发起通信,第一步同样是创建Socket套接字,但与TCP不同,UDP不建立连接,它是“无连接”的协议。客户端不需要预先连接目标地址,它只需要准备好要发送的消息,指定目标的IP地址和端口,然后就可以将数据报文(Datagram)直接发出。需要注意的是,由于UDP不保证可靠传输,因此丢包、顺序错乱或者重复发送都是可能出现的情况。
另一方面,UDP服务器的工作更像一个随时准备接收包裹的邮筒。它需要创建UDP Socket,并绑定在指定的IP和端口上(这一点和TCP服务端类似)。不过,它不需要监听或者接受连接请求,它所要做的,就是不断地接收来自任意来源的数据报。这些数据报可能来自任何客户端,彼此之间没有状态记录,因此每一条消息都应被视作独立处理。
一旦UDP服务器接收到客户端的消息,它可以选择是否回应,如果回应,那么也需要指明目标地址(通常就是消息来源)。因为UDP的“无连接”性质,每一次通信都是“即发即收”,不保证对方一定在线或一定收到。
典型UDP服务使用知名端口如53(DNS),而我们在后续的例子中将尝试使用随机端口,模拟UDP服务端和客户端之间的简单通信。
了解了这个基本模型,我们就可以借助 Boost ASIO 来尝试构建这个轻量级的UDP通信系统了。我们即将展示的代码,会用最小化的结构完成一次完整的UDP消息发送与接收过程。
UDP客户端
UDP的客户端跟TCP的可以说是大差不差
#include <boost/asio.hpp>
#include <boost/asio/ip/udp.hpp>
#include <exception>
#include <print>using boost::asio::ip::udp;
int main(int argc, char* argv[]) {if (argc != 2) {std::println("Error usage!");return -1;}try {boost::asio::io_context io_context;udp::resolver solver(io_context);auto receiver_endpoint = *solver.resolve(udp::v4(), argv[1], "daytime").begin();udp::socket socket(io_context);socket.open(udp::v4());std::array<char, 1> send_buf = { { 0 } };socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);std::array<char, 128> recv_buf;udp::endpoint sender_endpoint;socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);std::println("receive: {}", recv_buf.data());} catch (const std::exception& e) {std::println("Exception occurs: {}", e.what());}
}
值得注意的是,这里我们需要发一个空载的包刺激对方UDP服务器,这是因为UDP通信中缺乏了状态薄记,所以,我们需要自己通知对面,我们的通信开始了。当然,您可以自己设置自己的一套起始动作。来确保通信的有效性。
UDP同步服务器
#include <boost/asio.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <chrono>
#include <exception>
#include <format>
#include <print>
using namespace std::chrono;using my_time_t = std::chrono::time_point<std::chrono::system_clock,std::chrono::nanoseconds>;
static inline std::string __make_format_date(const year_month_day& ymd,const hh_mm_ss<minutes>& time) {return std::format("{}-{:02}-{:02} {:02}:{:02}",static_cast<int>(ymd.year()),static_cast<unsigned>(ymd.month()),static_cast<unsigned>(ymd.day()),time.hours().count(),time.minutes().count());
}
std::string
current_time() {using namespace std;using namespace std::chrono;const my_time_t raw_time = std::chrono::system_clock::now();auto now = floor<minutes>(raw_time);auto days_part = floor<days>(now);year_month_day ymd = year_month_day { days_part };auto time_since_midnight = now - days_part;hh_mm_ss time { time_since_midnight };return __make_format_date(ymd, time);
}
using boost::asio::ip::udp;int main() {try {boost::asio::io_context io_context;udp::socket socket(io_context, udp::endpoint(udp::v4(), 13));for (;;) {std::array<char, 1> recv_buf;udp::endpoint remote_endpoint;socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint);std::string message = current_time();boost::system::error_code ignored_error;socket.send_to(boost::asio::buffer(message),remote_endpoint, 0, ignored_error);}} catch (const std::exception& e) {std::println("Exception occurs: {}", e.what());}
}
作为对称,我们在一个循环中,只是监听潜在道来的包remote_endpoint
,这里没做校验,我们也不关心如何处理后续的远程UDP通信节点。因此这里只是一个占位符而已。
之后,我们粗暴的将信息直接丢给我们的客户端。
完成上述的代码编写之后
[charliechen@Charliechen build]$ sudo ./server
[sudo] password for charliechen:
[charliechen@Charliechen build]$ ./client 127.0.0.1
receive: 2025-06-08 12:48
[charliechen@Charliechen build]$
可以看到完成了一次经典的UDP通信了。
UDP异步服务器
#include <boost/asio.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <chrono>
#include <cstddef>
#include <exception>
#include <format>
#include <print>using boost::asio::ip::udp;using namespace std::chrono;using my_time_t = std::chrono::time_point<std::chrono::system_clock,std::chrono::nanoseconds>;
static inline std::string __make_format_date(const year_month_day& ymd,const hh_mm_ss<minutes>& time) {return std::format("{}-{:02}-{:02} {:02}:{:02}",static_cast<int>(ymd.year()),static_cast<unsigned>(ymd.month()),static_cast<unsigned>(ymd.day()),time.hours().count(),time.minutes().count());
}
std::string
current_time() {using namespace std;using namespace std::chrono;const my_time_t raw_time = std::chrono::system_clock::now();auto now = floor<minutes>(raw_time);auto days_part = floor<days>(now);year_month_day ymd = year_month_day { days_part };auto time_since_midnight = now - days_part;hh_mm_ss time { time_since_midnight };return __make_format_date(ymd, time);
}class udp_server {
public:udp_server(): socket(io_context, udp::endpoint(udp::v4(), 13)) {process_receive();}void run() {io_context.run();}private:void process_receive() {std::array<char, 1> recv_buffer;socket.async_receive_from(boost::asio::buffer(recv_buffer),remote_endpoint,[this](const boost::system::error_code& error, size_t bytes) {if (error) {return;}std::shared_ptr<std::string> message(new std::string(current_time()));socket.async_send_to(boost::asio::buffer(*message), remote_endpoint,[this, message](const boost::system::error_code&, std::size_t cnt) {std::println("Process message {}, bytes cnt: {}", *message, cnt);});process_receive();});;}boost::asio::io_context io_context;udp::socket socket;udp::endpoint remote_endpoint;
};int main() {try {udp_server server;server.run();} catch (const std::exception& e) {std::println("Exception occurs: {}", e.what());}
}
这里的步骤跟我们之前做
- 启动 UDP 服务器。
- 通过
async_receive_from()
等待客户端请求。 - 收到请求后:
- 构造时间字符串;
- 异步发送给客户端;
- 打印日志;
- 继续监听其他请求。
- 主线程通过
run()
阻塞等待事件处理。
我们简单的抽象一下上面的代码:
class udp_server {
public:udp_server(): socket(io_context, udp::endpoint(udp::v4(), 13)){process_receive();}void run() {io_context.run();}private:void process_receive() { /* ... */ }boost::asio::io_context io_context;udp::socket socket;udp::endpoint remote_endpoint;
};
io_context
:事件循环核心。socket
:绑定到 UDP 端口 13,用于接收和发送数据。remote_endpoint
:用于记录客户端的地址与端口,便于回传消息。
构造函数:初始化与接收启动
udp_server(): socket(io_context, udp::endpoint(udp::v4(), 13))
{process_receive();
}
- 创建一个监听 UDP 13 端口的 socket;
- 启动异步接收过程,等待客户端发送数据。
🔁 process_receive() 方法详解
void process_receive() {std::array<char, 1> recv_buffer;socket.async_receive_from(boost::asio::buffer(recv_buffer),remote_endpoint,[this](auto& error, std::size_t bytes) {if (error) return;// 收到客户端消息后准备发送响应auto message = std::make_shared<std::string>(current_time());socket.async_send_to(boost::asio::buffer(*message),remote_endpoint,[this, message](auto& /*ec*/, std::size_t cnt) {std::println("Process message {}, bytes cnt: {}", *message, cnt);});// 再次启动接收process_receive();});
}
recv_buffer
:申请 1 字节的接收缓冲;通常客户端只需触发一次接收,所以大小设置为 1 即可。async_receive_from
异步接收调用,完成后由 lambda 处理:- 若无错误,生成当前时间的字符串并包装成
shared_ptr
,确保其在异步发送期间有效。 - 调用
async_send_to
向先前的remote_endpoint
回应时间信息; - 回调中打印发送的内容与字节数;
- 最后重新调用
process_receive()
,保持服务器持续接收能力。
- 若无错误,生成当前时间的字符串并包装成
⚠️ 每次接收成功后都会递归启动下一次接收,确保可持续处理多个客户端请求。
⏱️ 运行方法
void run() {io_context.run();
}
这一调用在主线程中启动异步事件循环,直到没有待处理的操作为止(本例会一直运行,因为持续触发 process_receive()
)。
相关文章:
Boost ASIO 库深入学习(3)
Boost ASIO 库深入学习(3) UDP简单通信导论 在继续深入前,我们不妨也来点碎碎念,因为UDP通信协议的模型与TCP是不同的,这种差异正是理解“无连接通信”的关键所在。我们下面要构建的,是一个经典的UDP通信…...
【如何做好应用架构?】
一、应用架构定义 应用架构描述了各种用于支持业务架构并对数据架构所定义的各种数据进行出来的应用功能。这些应该功能指的是用来管理在数据架构中定义的数据,并对业务架构中定义的各项业务功能进行支持的能力。 其核心目标是确保应用系统高效、灵活、安全的支撑…...

1 Studying《蓝牙核心规范5.3》
目录 [Vol 0][Part B 蓝牙规范要求] 3 定义 3.1 蓝牙产品类型 4 核心配置 4.1 基本速率核心配置 4.2 增强型数据速率核心配置 4.4 低功耗核心配置 4.5 基本速率和低功耗结合的核心配置 4.6 主机控制器接口核心配置 [Vol 1][Part A 架构]1 概述 1.1 BR/EDR操作概述 …...

STM32+MPU6050传感器
#创作灵感## 在嵌入式系统开发中,STM32F103C8T6单片机与MPU6050传感器的组合因其高性能、低功耗以及丰富的功能而备受青睐。本文将简单介绍如何在Keil 5开发环境中实现STM32F103C8T6与MPU6050的连接和基本数据采集,带你快速入门智能硬件开发。 一、硬件…...
el-input限制输入数字,输入中文后数字校验失效
想要的效果:默认值为0,只能输入0-100的数字。 实现方式如下,使用 οnkeyup"this.valuethis.value.replace(/\D/g,‘’)"限制只能输入数字,输入数字没有问题,使用input实现数字不以0开头,也只能是…...

26考研——数据的表示和运算_整数和实数的表示(2)
408答疑 文章目录 二、整数和实数的表示1、整数的表示1.1、无符号整数的表示1.2、有符号整数的表示1.3、C 语言中的整数类型及类型转换1.3.1、C 语言中的整型数据类型1.3.2、有符号数和无符号数的转换1.3.3、不同字长整数之间的转换 2、实数的表示2.1、浮点数的相关概念2.2、浮…...
用 Lazarus IDE 写一个邮件客户端软件,能收发邮件,编写邮件
下面是一个使用Lazarus IDE开发的基本邮件客户端实现方案,包含收发邮件和编写邮件的核心功能。我们将使用Synapse库(跨平台的网络通信库)来处理邮件协议。 步骤1:安装依赖 安装Synapse库: 下载地址:https:…...

关于智能体API参考接口
关于智能体在Flask的源码:请求体(在payload里的是请求体)、请求头(在headers里的i局势请求头)。 我的例子: 我的疑问:为什么没按Coze官方API文档格式,在Apifox里发POST请求却能收到回复? 1. 你…...
命令行运行python程序报错 ImportError: /lib/x86_64-linux-gnu/libstdc++.so.6
命令行运行python程序报错 ImportError: /lib/x86_64-linux-gnu/libstdc.so.6 ImportError: /lib/x86_64-linux-gnu/libstdc.so.6: version GLIBCXX_3.4.29’ not found (required by /home/zitong/miniconda3/envs/torch112/lib/python3.9/site-packages/scipy/spatial/_ckdt…...

直角坐标系和斜角坐标系
前情概要 笛卡尔坐标系是直角坐标系和斜角坐标系的统称。为什么会有这两种坐标系呢,教材中为什么最后只用直角坐标系呢?我们这样解释: 研究一维空间中的向量时,由于一维空间中的向量有无数条,如果我们选定一条作为基…...

vmware 设置 dns
vmware 设置 dns 常用的 DNS(Domain Name System)服务器地址可以帮助你更快、更安全地解析域名。以下是一些国内外常用的公共 DNS 服务: 国内常用 DNS 阿里云 DNS IPv4: 223.5.5.5、223.6.6.6IPv6: 2400:3200::1、2400:3200:baba::1特点&am…...

基于单片机的病房呼叫系统(源码+仿真)
该系统由以 STM32F4 为平台的监控终端以及以 CC2530 为平台的无线传感网组成。系统上电后自动完成 ZigBee 网络的组建、终端节点的加入,病人可利用便携式的病人终端发出呼叫求助请求信息、节点在线信息以及对护士的服务评价信息等,这些信息通过路由节点发…...
React从基础入门到高级实战:React 实战项目 - 项目四:企业级仪表盘
React 实战项目:企业级仪表盘 欢迎来到 React 开发教程专栏 的第 29 篇!在前 28 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和实时通信等核心内容。这一次,我…...

基于微信小程序的睡眠宝系统源码数据库文档
摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,睡眠宝系统被用户普遍使用,为方便用户能够可以…...

VibePlayer
源代码地址: VibePlayer: VibePlayer是一款功能强大的Android音乐播放器应用,专为音乐爱好者设计,提供了丰富的音乐播放和管理功能。 用户需求 VibePlayer是一款功能强大的Android音乐播放器应用,专为音乐爱好者设计࿰…...

【汇编逆向系列】三、函数调用包含单个参数之float类型-xmm0寄存器,sub,rep,stos,movss,mulss,addss指令
一、汇编代码 single_float_param:0000000000000060: F3 0F 11 44 24 08 movss dword ptr [rsp8],xmm00000000000000066: 57 push rdi0000000000000067: 48 83 EC 10 sub rsp,10h000000000000006B: 48 8B FC mov …...
JAVA开发工具——IntelliJ IDEA
JAVA开发工具——IntelliJ IDEA 软件下载地址https://www.jetbrains.com/idea/ IDEA项目结构介绍 项目(project)模块(module)包(package)类(class) 包含关系:项目 > 模块 >…...

基于fpga的疲劳驾驶检测
基于fpga的疲劳驾驶检测 前言一、系统硬件设计二、系统软件设计系统上板实验测试 前言 代码基于网络大佬代码进行修改的。限制性比较大,不太灵活,当个本科毕业设计还是够的。 基于FPGA的疲劳检测模块硬件设计以FPGA核心控制模块为中心,通过…...

感谢阿里云RDS产品及时的“光速服务”
❝ 开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共3000人左右…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(十一)
下载buildroot https://buildroot.org/download.html下载交叉工具链 使用ST官方交叉工具链的话,在buildroot配置外部工具会有问题,所以直接使用正点原子的交叉编译工具 buildroot构建根文件系统 - 参考正点原子 配置 buildroot tar -vxf buildroot-20…...
18-Oracle 23ai JSON二元性颠覆传统
在当今百花齐放的多模型数据库时代,开发人员常在关系型与文档型数据库间艰难取舍。Oracle Database 23ai推出的JSON关系二元性(JSON Relational Duality) 和二元性视图(Duality Views) 创新性地统一了两者优势…...

Linux68 FTP 测试 上传下载
6.在vi编辑器里,哪个命令能将光标移到第200行?( B ) 7.A、200g B、:200 C、g200 D、G200 假如您需要找出 /etc/my.conf 文件属于哪个包 (package) ,您可以执行( D )C A、 rpm -q /etc/my.co…...

山东大学《数据可视化》期末复习宝典
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏:🏀山东大学期末速通专用_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1…...

【Elasticsearch】映射:Join 类型、Flattened 类型、多表关联设计
映射:Join 类型、Flattened 类型、多表关联设计 1.Join 类型1.1 主要应用场景1.1.1 一对多关系建模1.1.2 多层级关系建模1.1.3 需要独立更新子文档的场景1.1.4 文档分离但需要关联查询 1.2 使用注意事项1.3 与 Nested 类型的区别 2.Flattened 类型2.1 实际运用场景和…...

SpringBoot十二、SpringBoot系列web篇之过滤器Filte详解
一、前言 JavaWeb三大组件Servlet、Filter、Listener,其中之一便是过滤器Filter。 其实,Filter我们平常用的不多,一般多为项目初期搭建web架构的时候使用,后面用的就少了,在日常业务开发中不太可能碰到需要手写Filte…...

【RTSP从零实践】1、根据RTSP协议实现一个RTSP服务
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...

行为设计模式之Iterator(迭代器)
行为设计模式之Iterator(迭代器) 摘要: 迭代器模式(Iterator)是一种行为设计模式,它提供顺序访问聚合对象元素的方法,同时不暴露内部结构。该模式由迭代器接口(Iterator)、具体迭代器(ConcreteIterator)、聚合接口(Ag…...

FPGA点亮ILI9488驱动的SPI+RGB接口LCD显示屏(一)
FPGA点亮ILI9488驱动的SPIRGB接口LCD显示屏 ILI9488 RGB接口初始化 目录 前言 一、ILI9488简介 二、3线SPI接口简介 三、配置寄存器介绍 四、手册和初始化verilog FPGA代码 总结 前言 ILI9488是一款广泛应用于嵌入式系统和电子设备的彩色TFT LCD显示控制器芯片。本文将介…...
6板块公共数据典型应用场景【政务服务|公共安全|公共卫生|环境保护|金融风控|教育科研]
1. 政务服务 1.1 城市规划与管理 公共数据在城市规划与管理中可发挥关键作用。通过汇聚自然资源、建筑物、人口分布等基础数据,构建数字孪生城市模型,辅助城市总体规划编制、决策仿真模拟。在城市基础设施建设、安全运营、应急管理等方面,公共数据也是不可或缺的基础支撑。例…...

如何实现本地mqtt服务器和云端服务器同步?
有时候,一个物联网项目,A客户想要本地使用,B客户想要线上使用,C客户想要本地部署,当有网环境时能线上使用。这个时候就需要本地MQTT服务和线上MQTT服务能相互自动转发。 后来经我一翻研究,其实Activemq支持…...