Boost
是一个开源的 C++ 库集合,提供了许多实用的功能,如网络编程、图形界面、数学计算等。Boost 库可以与标准 C++ 库无缝集成,扩展了 C++ 的功能。
# 安装 Boost 库
通过 APT 包管理器安装 Boost 库:
# 1. 更新软件包索引 | |
sudo apt update | |
# 2. 安装 Boost 核心库和开发文件(包含头文件和静态 / 动态库) | |
sudo apt install libboost-all-dev -y | |
# 3. 验证安装(检查版本) | |
boostversion=$(cat /usr/include/boost/version.hpp | grep "#define BOOST_VERSION" | awk '{print $3}') | |
echo "Boost 版本: $boostversion" |
# 基本使用
同步操作会阻塞当前线程,直到操作完成。异步操作则不会阻塞线程,通过回调函数在操作完成后触发。
# 同步
#include <iostream> | |
#include<boost/asio.hpp> | |
int main(){ | |
boost::asio::io_context io; | |
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); | |
// 同步,阻塞线程 5 秒 | |
t.wait(); | |
//wait () 返回,定时器资源(如系统句柄)被自动释放 | |
std::cout<<"Hello,world!"<<std::endl; | |
return 0; | |
} |
定时器
steady_timer
是 Boost.Asio
提供的定时器类,用于在指定时间后执行回调函数。
steady_timer
的构造函数需要 io_context
参数,目的是 显式声明 该定时器依赖 ** 的事件循环。这种设计确保类型安全,避免隐式关联导致的错误。
io_context 的作用
io_context
是 Boost.Asio
的核心组件,负责调度以及执行所有异步(如 DNS 解析、连接、读写)和同步操作的生命周期。(如定时器、网络 I/O)。
# 异步
异步操作需要事件循环来驱动, io_context::run()
方法启动事件处理流程。
#include <iostream> | |
#include<boost/asio.hpp> | |
void print(const boost::system::error_code& a /*e*/) | |
{ | |
std::cout<<a<<std::endl; | |
std::cout<<"Hello,world!"<<std::endl; | |
} | |
int main(){ | |
boost::asio::io_context io; | |
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5)); | |
t.async_wait(&print); // 异步,不会阻塞线程,回调函数 print 会在 5 秒后执行 | |
io.run(); | |
return 0; | |
} |
io.run()
是异步编程的核心,负责监听操作系统异步操作(如定时器到期、网络请求完成)的完成事件,确保资源(如 定时器
、 socket
)被正确释放,并触发回调函数。
特性 | 同步代码 | 异步代码 |
---|---|---|
执行方式 | 阻塞等待( wait() ) |
非阻塞,通过回调触发 |
事件循环 | 无需 io.run() |
必须调用 io.run() |
线程占用 | 单线程阻塞 | 单线程 / 多线程(由 run() 决定) |
适用场景 | 简单、短耗时操作 | 高并发、实时性要求高的场景 |
时间点计算
- 函数式编程
#include <functional> | |
#include <iostream> | |
#include <boost/asio.hpp> | |
void print(const boost::system::error_code& /*e*/, | |
boost::asio::steady_timer* t, int* count) | |
{ | |
if (*count < 5) | |
{ | |
std::cout << *count << std::endl; | |
++(*count); | |
// 新到期时间 = 当前到期时间 + 1 秒 | |
t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); | |
// 异步等待新到期时间 | |
t->async_wait(std::bind(print, | |
std::placeholders::_1, t, count)); | |
} | |
} | |
int main() | |
{ | |
boost::asio::io_context io; | |
int count = 0; | |
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1)); | |
t.async_wait(std::bind(print, | |
std::placeholders::_1, &t, &count)); | |
io.run(); | |
std::cout << "Final count is " << count << std::endl; | |
return 0; | |
} | |
// 输出 | |
// 0 | |
// 1 | |
// 2 | |
// 3 | |
// 4 | |
// Final count is 5 |
// 新到期时间 = 当前到期时间 + 1 秒 | |
t->expires_at(t->expires_at() + boost::asio::chrono::seconds(1)); |
t->expires_at()
返回定时器当前的到期时间点( system_clock::time_point
类型), boost::asio::chrono::seconds(1)
表示一个 时间间隔( duration
类型)。两者相加会生成新的到期时间点:
这种设计允许动态调整定时器的触发时间,适用于需要重复执行任务的场景(如每隔 1
秒打印一次计数器)。
类型匹配的底层原理
expires_at()
的参数类型是 system_clock::time_point
,而 boost::asio::chrono::seconds(1)
的类型是 duration
, 明确指定了时间单位(秒),避免了因隐式类型转换导致的单位混淆(如误用毫秒或微秒)。
Boost.Asio
的 chrono
库通过 类型推导 和 运算符重载,自动将 duration
转换为 time_point
,确保表达式合法。
若直接使用 t->expires_at(1)
,编译器会报错,因为 1
的类型是 int
,无法隐式转换为 time_point
。
- 面向对象编程
#include <functional> | |
#include <iostream> | |
#include <boost/asio.hpp> | |
class printer | |
{ | |
public: | |
// 构造函数,初始化 timer_成员和 count_成员 | |
printer(boost::asio::io_context& io) | |
: timer_(io, boost::asio::chrono::seconds(1)), // 初始化 timer_成员 | |
count_(0) // 初始化 count_成员 | |
{ | |
//bind () 函数将成员函数 print 绑定到当前对象上,并作为回调函数传递给 timer_.async_wait () | |
timer_.async_wait(std::bind(&printer::print, this)); | |
} | |
// 析构函数 | |
~printer() | |
{ | |
std::cout<< "final count is "<< count_ <<std::endl; | |
} | |
void print(){ | |
if(count_<5) | |
{ | |
std::cout << count_ <<std::endl; | |
++count_; | |
// 新到期时间 = 当前到期时间 + 1 秒 | |
timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1)); | |
timer_.async_wait(std::bind(&printer::print,this)); | |
} | |
} | |
private: | |
boost::asio::steady_timer timer_; | |
int count_; | |
}; | |
int main() | |
{ | |
boost::asio::io_context io; | |
printer p(io); | |
io.run(); | |
return 0; | |
} | |
// 输出 | |
// 0 | |
// 1 | |
// 2 | |
// 3 | |
// 4 | |
// final count is 5 |
优缺点对比
维度 | 函数式 | 面向对象 |
---|---|---|
代码简洁性 | ✅ 代码简短,适合简单场景 | ❌ 类定义增加代码量 |
安全性 | ❌ 需手动管理指针,易出错 | ✅ 自动管理资源,避免悬垂指针 |
扩展性 | ❌ 修改需调整参数传递链 | ✅ 新增功能只需扩展类方法 |
异步上下文 | ❌ 依赖外部变量,多线程下可能竞争 | ✅ 成员变量天然隔离,适合多线程异步任务 |
性能开销 | ✅ 无虚函数或类结构开销 | ❌ 类实例化带来微小内存开销 |
线程安全的异步任务队列(通过 Strand 串行化)
#include <functional> | |
#include <iostream> | |
#include <thread> | |
#include <boost/asio.hpp> | |
// 通过 strand_ 序列化回调函数的执行 | |
class printer | |
{ | |
public: | |
printer(boost::asio::io_context& io) | |
: strand_(boost::asio::make_strand(io)), // 初始化 strand_成员 | |
timer1_(io, boost::asio::chrono::seconds(1)),// 初始化 timer1_成员 | |
timer2_(io, boost::asio::chrono::seconds(1)),// 初始化 timer2_成员 | |
count_(0) | |
{ | |
// 使用 strand_串行化异步操作,bind_executor () 将异步操作绑定到 strand_上 | |
timer1_.async_wait(boost::asio::bind_executor(strand_, | |
std::bind(&printer::print1, this))); | |
timer2_.async_wait(boost::asio::bind_executor(strand_, | |
std::bind(&printer::print2, this))); | |
} | |
~printer() | |
{ | |
std::cout << "Final count is " << count_ << std::endl; | |
} | |
void print1() | |
{ | |
if (count_ < 10) | |
{ | |
std::cout << "Timer 1: " << count_ << std::endl; | |
++count_; | |
timer1_.expires_at(timer1_.expiry() + boost::asio::chrono::seconds(1)); | |
timer1_.async_wait(boost::asio::bind_executor(strand_, | |
std::bind(&printer::print1, this))); | |
} | |
} | |
void print2() | |
{ | |
if (count_ < 10) | |
{ | |
std::cout << "Timer 2: " << count_ << std::endl; | |
++count_; | |
timer2_.expires_at(timer2_.expiry() + boost::asio::chrono::seconds(1)); | |
timer2_.async_wait(boost::asio::bind_executor(strand_, | |
std::bind(&printer::print2, this))); | |
} | |
} | |
private: | |
boost::asio::strand<boost::asio::io_context::executor_type> strand_; | |
boost::asio::steady_timer timer1_; | |
boost::asio::steady_timer timer2_; | |
int count_; | |
}; | |
int main() | |
{ | |
boost::asio::io_context io; | |
printer p(io); | |
// 创建一个新线程,执行 Lambda 表达式中的代码 (即 io.run ()) | |
std::thread t([&]{ io.run(); }); | |
io.run(); | |
// 阻塞主线程,直到 t 线程结束 | |
t.join(); | |
return 0; | |
} |
代码通过多线程运行 io_context
的事件循环,结合 strand_
的序列化机制,实现了高效的事件响应与线程安全的逻辑处理。虽然有两个线程处理事件,但任务逻辑( print1
/ print2
)本质上是单线程化的,因此更准确地说,这是多线程协作处理事件循环,但任务执行是序列化的。
Lambda
的捕获方式 [&]
- [&] 表示以引用方式捕获所有外部变量。
在Lambda
内部可以直接访问外部作用域的所有变量(如io
),且操作的是原变量本身,而非副本。 - 对比其他捕获方式:
[=]:以值方式捕获(创建副本,可能影响性能)。
[io]:仅捕获 io 变量(需显式指定)。
此处 [&] 简化了代码,但需确保Lambda
生命周期内外部变量有效。
- 代码的多线程机制
线程数量与任务类型
代码中通过std::thread t([&]{ io.run(); })
创建了一个工作线程,主线程也调用io.run()
,因此 总共有两个线程运行io_context
的事件循环。这两个线程会并行处理io_context
中的异步操作(如定时器到期事件)。
异步操作的并行性
timer1_
和 timer2_
是独立的定时器,它们的异步等待操作会被提交到 io_context
的任务队列中。理论上,这两个定时器的到期事件可能被分配到不同的线程处理。但由于绑定了 strand_
,所有回调会被强制序列化执行,实际表现为单线程顺序处理。
- Strand 的核心作用
线程安全与顺序保证
strand_
是Boost.Asio
提供的一种隐式同步机制,其作用包括:
序列化回调执行:所有通过 strand_
提交的回调(如 print1
和 print2
)会按提交顺序依次执行,即使多个线程在运行 io_context
。
消除数据竞争:对共享资源 count_
的修改(如 ++count_
)会被限制在同一个线程上下文中,无需显式加锁。
代码中的具体实现
timer1_.async_wait(boost::asio::bind_executor(strand_, ...)); |
将定时器的回调函数绑定到 strand_
的执行器上,确保回调在 strand_
的调度下运行。因此,即使两个定时器的到期事件被不同线程触发, print1
和 print2
也不会并发执行。
- 执行流程示例
假设两个定时器同时到期:- 事件触发:
timer1_
和timer2_
的到期事件被提交到io_context
。 - 线程分配:事件可能被主线程或工作线程
t
处理。 - Strand 调度:无论哪个线程处理事件,回调函数会被
strand_
序列化。例如:- 线程 1 执行
print1
→ 输出Timer 1: 0
。 - 线程 2 执行
print2
→ 但需等待print1
完成后才能执行,输出Timer 2: 1
。 - 最终
count_
会严格递增至 10,无并发问题
- 线程 1 执行
- 事件触发:
# TCP
TCP 连接
- TCP 连接是面向连接的,可靠的,基于字节流的传输层通信协议。
- TCP 连接需要三次握手来建立连接,四次挥手来关闭连接。
- TCP 连接的建立和关闭都需要消耗一定的资源,因此需要合理管理 TCP 连接。
- TCP 连接的建立和关闭都需要消耗一定的资源,因此需要合理管理 TCP 连接。
UDP 连接
- UDP 连接是无连接的,不可靠的,基于数据报的传输层通信协议。
- UDP 连接不需要建立连接,只需要发送数据报即可。
- UDP 连接不需要关闭连接,只需要发送数据报即可。
- UDP 连接的建立和关闭不需要消耗资源,因此不需要管理 UDP 连接。
TCP 和 UDP 的区别
TCP | UDP |
---|---|
面向连接 | 无连接 |
可靠 | 不可靠 |
基于字节流 | 基于数据报 |
需要三次握手 | 不需要握手 |
需要四次挥手 | 不需要挥手 |
建立和关闭连接需要消耗资源 | 建立和关闭连接不需要消耗资源 |
需要管理连接 | 不需要管理连接 |
TCP 和 UDP 的应用场景
- TCP 连接适用于需要可靠传输的场景,如文件传输、邮件发送等。
- UDP 连接适用于需要快速传输的场景,如视频直播、实时游戏等。
TCP 和 UDP 的编程模型
- TCP 连接的编程模型通常包括客户端和服务端两部分,客户端通过
socket
函数创建一个套接字,然后通过connect
函数连接到服务端,然后通过send
和recv
函数进行数据的发送和接收,最后通过close
函数关闭连接。
# 同步 TCP 时间服务器
- 命令行参数
int main(int argc, char* argv[]){} |
argc(Argument Count)
表示命令行参数的总数量(包括程序名本身)。
- 例如,若用户输入
./myprogram arg1 arg2
,则argc
的值为 3(程序名 + 2 个参数)。
argv[](Argument Vector)
是一个字符串指针数组,存储所有命令行参数的具体值。
argv[0]
:程序自身的名称(如 “./myprogram”)。argv[1]
~argv[argc-1]
:用户输入的其他参数。argv[argc]
:固定为 NULL(表示数组结束)。
示例代码:
#include <stdio.h> | |
int main(int argc, char* argv[]) { | |
printf("参数总数 argc = %d\n", argc); | |
for (int i = 0; i < argc; i++) { | |
printf("argv[%d] = %s\n", i, argv[i]); | |
} | |
return 0; | |
} |
运行示例:
./myprogram arg1 arg2 arg3 |
输出:
参数总数 argc = 4 | |
argv[0] = ./myprogram | |
argv[1] = arg1 | |
argv[2] = arg2 | |
argv[3] = arg3 |
- 同步 TCP 服务端
服务端:监听端口 13 , 发送时间数据
#include <ctime> | |
#include <iostream> | |
#include <string> | |
#include <boost/asio.hpp> | |
using boost::asio::ip::tcp; | |
std::string make_daytime_string() { | |
std::time_t now = std::time(nullptr); | |
return std::ctime(&now); // 格式示例: "Wed Oct 2 00:00:00 2023\n" | |
} | |
int main() { | |
try { | |
boost::asio::io_context io_context; | |
// 监听端口 13 (需管理员权限) 或改用其他端口如 12345 | |
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13)); | |
std::cout << "Daytime 服务器启动,监听端口: " << acceptor.local_endpoint().port() << std::endl; | |
for (;;) { | |
tcp::socket socket(io_context); | |
acceptor.accept(socket); // 阻塞等待客户端连接 | |
std::cout << "客户端连接: " << socket.remote_endpoint() << std::endl; | |
std::string message = make_daytime_string(); | |
boost::system::error_code ignored_error; | |
// 发送时间数据后立即关闭连接 | |
boost::asio::write(socket, boost::asio::buffer(message), ignored_error); | |
} | |
} catch (std::exception& e) { | |
std::cerr << "异常: " << e.what() << std::endl; | |
} | |
return 0; | |
} |
- 同步 TCP 客户端
客户端:连接服务器,发送数据,接收响应,关闭连接。
整体流程
- **初始化 I/O 上下文:** 创建 io_context 对象,管理所有 I/O 操作。
- **解析目标地址:** 将用户输入的主机名和服务名转换为具体的网络地址列表。
- **创建套接字:** 准备用于通信的套接字对象。
- **连接服务器:** 按地址列表尝试连接,直到成功或全部失败。
#include <array> | |
#include <iostream> | |
#include <boost/asio.hpp> | |
//using 导入某个变量到当前作用域 | |
using boost::asio::ip::tcp; //tcp 命名空间 | |
int main(int argc, char* argv[]) | |
{ | |
try | |
{ | |
if (argc != 2) | |
{ | |
//std::cerr 通常用于输出错误信息,这些信息不会被重定向到文件或管道,而是直接显示在终端上。 | |
std::cerr << "Usage: client <host>" << std::endl; | |
return 1; | |
} | |
boost::asio::io_context io_context; | |
// 创建解析器对象,用于将 主机名 和 服务名 / 端口号 转换为具体的网络地址(IP + 端口)。 | |
tcp::resolver resolver(io_context); //io_context:绑定到同一个 I/O 上下文,确保解析操作由该上下文管理。 | |
tcp::resolver::results_type endpoints = | |
resolver.resolve(argv[1], "cc"); // 解析用户输入的主机名和服务名,生成可能的网络端点列表。 | |
tcp::socket socket(io_context); // 套接字绑定到同一个 I/O 上下文,确保其操作由该上下文管理。 | |
boost::asio::connect(socket, endpoints); // 尝试连接到服务器。 | |
for (;;) | |
{ | |
std::array<char, 128> buf; | |
boost::system::error_code error; | |
//read_some 函数用于从 TCP 连接中读取数据。buffer 函数用于创建一个缓冲区,用于存储读取的数据。size_t 类型表示读取的字节数。 | |
size_t len = socket.read_some(boost::asio::buffer(buf), error); | |
if (error == boost::asio::error::eof) //eof 表示连接已关闭。 | |
break; // Connection closed cleanly by peer. | |
else if (error) | |
throw boost::system::system_error(error); // Some other error. | |
std::cout.write(buf.data(), len);//buf.data () 返回缓冲区的指针,len 表示读取的字节数。 | |
} | |
} | |
catch (std::exception& e) | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |
boost::asio::io_context io_context;
作用: 创建 I/O 上下文对象,管理所有底层 I/O 操作(如网络通信、定时器等)。
原理:
io_context
是Boost.Asio
的 事件循环核心,负责调度和处理异步操作。
所有异步操作(如连接、读写)都需要通过它执行。
tcp::resolver resolver(io_context);
作用: 创建解析器对象,用于将 主机名 和 服务名 / 端口号 转换为具体的网络地址(IP + 端口)。
参数:
io_context
:绑定到同一个 I/O 上下文,确保解析操作由该上下文管理。
功能:- 解析器 (
resolver
) 支持DNS
查询,将域名(如"example.com"
)解析为 IP 地址。 - 将服务名(如
"http"
)解析为端口号(如80
)。
tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "cc");
作用: 解析用户输入的主机名和服务名,生成可能的网络端点列表。
参数:
argv[1]
:命令行输入的主机名(如"localhost"
或"192.168.1.100"
)。"cc"
:服务名或端口号。这里可能是自定义的服务名(需系统配置)或直接表示端口号(如"12345"
)。
返回值:endpoints
是一个端点列表,包含所有可能的 IP 地址和端口组合(例如IPv4
和IPv6
地址)。
tcp::socket socket(io_context);
作用: 创建 TCP
套接字对象,用于与服务器通信。
参数:
io_context
:套接字绑定到同一个 I/O 上下文,确保其操作由该上下文管理。
功能:- 套接字是网络通信的端点,通过它可以发送和接收数据。
boost::asio::connect(socket, endpoints);
作用: 尝试连接到服务器。
参数:
socket
:上一步创建的套接字。endpoints
:解析得到的端点列表。
行为:- 按顺序尝试连接
endpoints
中的每一个端点(如IPv4
地址优先,失败后尝试IPv6
)。 - 当某个端点连接成功时,停止尝试。
- 如果所有端点均连接失败,抛出异常。
# 异步 TCP 时间服务器
- 主函数入口
int main() | |
{ | |
try | |
{ | |
boost::asio::io_context io_context; // 创建 IO 上下文,管理所有异步操作 | |
tcp_server server(io_context); // 创建 TCP 服务器实例,初始化监听 | |
io_context.run(); // 启动事件循环,处理异步操作 | |
} | |
catch (std::exception& e) // 异常处理 | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |
io_context
是Boost.Asio
的核心,负责调度异步任务。tcp_server
初始化时会开始监听连接。io_context.run()
会阻塞主线程,持续处理异步事件,直到所有任务完成。
- TCP 服务器初始化(tcp_server 构造函数)
tcp_server(boost::asio::io_context& io_context) | |
: io_context_(io_context), | |
acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) // 绑定 IPv4 地址的 13 端口 | |
{ | |
start_accept(); // 开始异步等待客户端连接 | |
} |
acceptor_
初始化时绑定到本地 13 端口。start_accept()
启动异步接受连接的过程。
- 异步接受连接(start_accept)
void start_accept() | |
{ | |
tcp_connection::pointer new_connection = tcp_connection::create(io_context_); // 创建新连接对象 | |
acceptor_.async_accept( // 异步接受连接 | |
new_connection->socket(), | |
[this, new_connection](const boost::system::error_code& error) { | |
handle_accept(error, new_connection); // 连接完成时调用处理函数 | |
}); | |
} |
tcp_connection::create
创建新的连接对象,其socket
由io_context
管理。async_accept
异步等待客户端连接。当有连接到来时,lambda
被调用,传递错误码和连接对象。
- 处理新连接(handle_accept)
void handle_accept(const boost::system::error_code& error, tcp_connection::pointer new_connection) | |
{ | |
if (!error) | |
{ | |
new_connection->start(); // 启动数据发送流程 | |
} | |
start_accept(); // 继续监听下一个连接 | |
} |
- 如果没有错误,调用
new_connection->start()
开始处理客户端请求。 - 无论是否出错,再次调用
start_accept()
,形成循环监听。
- TCP 连接处理(tcp_connection::start)
void start() | |
{ | |
message_ = make_daytime_string(); // 生成当前时间字符串 | |
auto self(shared_from_this()); // 获取 shared_ptr,延长对象生命周期 | |
boost::asio::async_write( // 异步发送数据 | |
socket_, | |
boost::asio::buffer(message_), | |
[this, self](const auto& error, size_t bytes) { | |
handle_write(error, bytes); // 发送完成后的回调 | |
}); | |
} |
make_daytime_string()
生成当前时间的字符串。shared_from_this()
确保在异步操作期间对象不会被销毁。async_write
异步发送数据,完成后调用handle_write
。
- 数据发送完成处理(handle_write)
void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) | |
{ | |
// 此处可添加发送完成后的逻辑,例如关闭连接 | |
} |
- 当前代码未处理发送结果,但可在此处添加错误处理或资源释放逻辑。
- 生成时间字符串(make_daytime_string)
std::string make_daytime_string() | |
{ | |
time_t now = time(0); | |
return ctime(&now); // 示例输出:"Wed May 22 15:42:36 2024\n" | |
} |
- 返回的字符串末尾包含换行符,符合 Daytime 协议标准。
异步执行流程总结:
- 服务器启动,监听端口 13。
- 当客户端连接时,async_accept 完成,触发 handle_accept。
- 无错误则调用 start () 发送时间数据。
- 数据发送完成后,由 handle_write 处理后续逻辑(当前为空)。
- 服务器循环调用 start_accept,持续监听新连接。
** 关键点:**
- 生命周期管理: 通过
shared_from_this()
和shared_ptr
确保异步操作期间对象存活。 - 异步链式调用:
async_accept
→handle_accept
→async_write
→handle_write
形成异步链。 - 端口重用: Daytime 协议使用 13 端口,需确保权限允许(Linux/Mac 可能需要 sudo 运行)。
完整代码:
#include <ctime> | |
#include <functional> | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
#include <boost/asio.hpp> | |
using boost::asio::ip::tcp; | |
std::string make_daytime_string() | |
{ | |
time_t now = time(0); | |
return ctime(&now); // 示例输出:"Wed May 22 15:42:36 2024\n" | |
} | |
class tcp_connection | |
: public std::enable_shared_from_this<tcp_connection> | |
{ | |
public: | |
typedef std::shared_ptr<tcp_connection> pointer; | |
static pointer create(boost::asio::io_context& io_context) | |
{ | |
return pointer(new tcp_connection(io_context)); | |
} | |
tcp::socket& socket() | |
{ | |
return socket_; | |
} | |
void start() | |
{ | |
message_ = make_daytime_string(); // 生成当前时间字符串 | |
auto self(shared_from_this()); // 获取 shared_ptr,延长对象生命周期 | |
boost::asio::async_write( // 异步发送数据 | |
socket_, | |
boost::asio::buffer(message_), | |
[this, self](const auto& error, size_t bytes) { | |
handle_write(error, bytes); // 发送完成后的回调 | |
}); | |
} | |
private: | |
tcp_connection(boost::asio::io_context& io_context) | |
: socket_(io_context) | |
{ | |
} | |
void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) | |
{ | |
// 此处可添加发送完成后的逻辑,例如关闭连接 | |
} | |
tcp::socket socket_; | |
std::string message_; | |
}; | |
class tcp_server | |
{ | |
public: | |
tcp_server(boost::asio::io_context& io_context) | |
:io_context_(io_context), | |
acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) // 绑定 IPv4 地址的 13 端口 | |
{ | |
start_accept(); // 开始异步等待客户端连接 | |
} | |
private: | |
void start_accept() { | |
tcp_connection::pointer new_connection = tcp_connection::create(io_context_);// 创建新连接对象 | |
acceptor_.async_accept( // 异步接受连接 | |
new_connection->socket(), | |
[this, new_connection](const boost::system::error_code& error) { | |
// 这里的 this 指向当前 tcp_server 对象 | |
handle_accept(error, new_connection); | |
} | |
); | |
} | |
void handle_accept(const boost::system::error_code& error, tcp_connection::pointer new_connection) | |
{ | |
if (!error) | |
{ | |
new_connection->start(); // 启动数据发送流程 | |
} | |
start_accept(); // 继续监听下一个连接 | |
} | |
boost::asio::io_context& io_context_; | |
tcp::acceptor acceptor_; | |
}; | |
int main() | |
{ | |
try | |
{ | |
boost::asio::io_context io_context; // 创建 IO 上下文,管理所有异步操作 | |
tcp_server server(io_context); // 创建 TCP 服务器实例,初始化监听 | |
io_context.run(); // 启动事件循环,处理异步操作 | |
} | |
catch (std::exception& e) // 异常处理 | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |
# UPD
# 同步 UDP 客户端
#include <array> | |
#include <iostream> | |
#include <boost/asio.hpp> | |
using boost::asio::ip::udp; | |
int main(int argc, char* argv[]) | |
{ | |
try | |
{ | |
if (argc != 2) | |
{ | |
std::cerr << "Usage: client <host>" << std::endl; | |
return 1; | |
} | |
boost::asio::io_context io_context; | |
udp::resolver resolver(io_context); | |
udp::endpoint receiver_endpoint = | |
*resolver.resolve(udp::v4(), argv[1], "daytime").begin(); | |
udp::socket socket(io_context); | |
socket.open(udp::v4()); | |
std::array<char, 1> send_buf = <!--swig0-->; | |
socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint); | |
std::array<char, 128> recv_buf; | |
udp::endpoint sender_endpoint; | |
size_t len = socket.receive_from( | |
boost::asio::buffer(recv_buf), sender_endpoint); | |
std::cout.write(recv_buf.data(), len); | |
} | |
catch (std::exception& e) | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |
# 异步 UDP 服务端
#include <array> | |
#include <ctime> | |
#include <functional> | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
#include <boost/asio.hpp> | |
using boost::asio::ip::udp; | |
std::string make_daytime_string() | |
{ | |
using namespace std; // For time_t, time and ctime; | |
time_t now = time(0); | |
return ctime(&now); | |
} | |
class udp_server | |
{ | |
public: | |
udp_server(boost::asio::io_context& io_context) | |
: socket_(io_context, udp::endpoint(udp::v4(), 13)) | |
{ | |
start_receive(); | |
} | |
private: | |
void start_receive() | |
{ | |
socket_.async_receive_from( | |
boost::asio::buffer(recv_buffer_), remote_endpoint_, | |
std::bind(&udp_server::handle_receive, this, | |
boost::asio::placeholders::error, | |
boost::asio::placeholders::bytes_transferred)); | |
} | |
void handle_receive(const boost::system::error_code& error, | |
std::size_t /*bytes_transferred*/) | |
{ | |
if (!error) | |
{ | |
std::shared_ptr<std::string> message( | |
new std::string(make_daytime_string())); | |
socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, | |
std::bind(&udp_server::handle_send, this, message, | |
boost::asio::placeholders::error, | |
boost::asio::placeholders::bytes_transferred)); | |
start_receive(); | |
} | |
} | |
void handle_send(std::shared_ptr<std::string> /*message*/, | |
const boost::system::error_code& /*error*/, | |
std::size_t /*bytes_transferred*/) | |
{ | |
} | |
udp::socket socket_; | |
udp::endpoint remote_endpoint_; | |
std::array<char, 1> recv_buffer_; | |
}; | |
int main() | |
{ | |
try | |
{ | |
boost::asio::io_context io_context; | |
udp_server server(io_context); | |
io_context.run(); | |
} | |
catch (std::exception& e) | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |
# 组合 TCP/UDP
#include <array> | |
#include <ctime> | |
#include <functional> | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
#include <boost/asio.hpp> | |
using boost::asio::ip::tcp; | |
using boost::asio::ip::udp; | |
std::string make_daytime_string() | |
{ | |
using namespace std; // For time_t, time and ctime; | |
time_t now = time(0); | |
return ctime(&now); | |
} | |
class tcp_connection | |
: public std::enable_shared_from_this<tcp_connection> | |
{ | |
public: | |
typedef std::shared_ptr<tcp_connection> pointer; | |
static pointer create(boost::asio::io_context& io_context) | |
{ | |
return pointer(new tcp_connection(io_context)); | |
} | |
tcp::socket& socket() | |
{ | |
return socket_; | |
} | |
void start() | |
{ | |
message_ = make_daytime_string(); | |
boost::asio::async_write(socket_, boost::asio::buffer(message_), | |
std::bind(&tcp_connection::handle_write, shared_from_this())); | |
} | |
private: | |
tcp_connection(boost::asio::io_context& io_context) | |
: socket_(io_context) | |
{ | |
} | |
void handle_write() | |
{ | |
} | |
tcp::socket socket_; | |
std::string message_; | |
}; | |
class tcp_server | |
{ | |
public: | |
tcp_server(boost::asio::io_context& io_context) | |
: io_context_(io_context), | |
acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) | |
{ | |
start_accept(); | |
} | |
private: | |
void start_accept() | |
{ | |
tcp_connection::pointer new_connection = | |
tcp_connection::create(io_context_); | |
acceptor_.async_accept(new_connection->socket(), | |
std::bind(&tcp_server::handle_accept, this, new_connection, | |
boost::asio::placeholders::error)); | |
} | |
void handle_accept(tcp_connection::pointer new_connection, | |
const boost::system::error_code& error) | |
{ | |
if (!error) | |
{ | |
new_connection->start(); | |
} | |
start_accept(); | |
} | |
boost::asio::io_context& io_context_; | |
tcp::acceptor acceptor_; | |
}; | |
class udp_server | |
{ | |
public: | |
udp_server(boost::asio::io_context& io_context) | |
: socket_(io_context, udp::endpoint(udp::v4(), 13)) | |
{ | |
start_receive(); | |
} | |
private: | |
void start_receive() | |
{ | |
socket_.async_receive_from( | |
boost::asio::buffer(recv_buffer_), remote_endpoint_, | |
std::bind(&udp_server::handle_receive, this, | |
boost::asio::placeholders::error)); | |
} | |
void handle_receive(const boost::system::error_code& error) | |
{ | |
if (!error) | |
{ | |
std::shared_ptr<std::string> message( | |
new std::string(make_daytime_string())); | |
socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_, | |
std::bind(&udp_server::handle_send, this, message)); | |
start_receive(); | |
} | |
} | |
void handle_send(std::shared_ptr<std::string> /*message*/) | |
{ | |
} | |
udp::socket socket_; | |
udp::endpoint remote_endpoint_; | |
std::array<char, 1> recv_buffer_; | |
}; | |
int main() | |
{ | |
try | |
{ | |
boost::asio::io_context io_context; | |
tcp_server server1(io_context); | |
udp_server server2(io_context); | |
io_context.run(); | |
} | |
catch (std::exception& e) | |
{ | |
std::cerr << e.what() << std::endl; | |
} | |
return 0; | |
} |