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 = {{ 0 }};
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;
}