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_timerBoost.Asio 提供的定时器类,用于在指定时间后执行回调函数。

steady_timer 的构造函数需要 io_context 参数,目的是 ​显式声明 该定时器依赖** 的事件循环。这种设计确保类型安全,避免隐式关联导致的错误。

io_context 的作用
io_contextBoost.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() 决定)
​适用场景 简单、短耗时操作 高并发、实时性要求高的场景

​时间点计算

  1. 函数式编程
#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.Asiochrono 库通过 ​类型推导运算符重载,自动将 duration 转换为 time_point,确保表达式合法。
若直接使用 t->expires_at(1),编译器会报错,因为 1 的类型是 int,无法隐式转换为 time_point

  1. 面向对象编程
#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 生命周期内外部变量有效。
  1. 代码的多线程机制
    ​线程数量与任务类型
    代码中通过 std::thread t([&]{ io.run(); }) 创建了一个工作线程,主线程也调用 io.run(),因此 ​总共有两个线程运行 io_context 的事件循环。这两个线程会并行处理 io_context 中的异步操作(如定时器到期事件)。

​异步操作的并行性
timer1_timer2_ 是独立的定时器,它们的异步等待操作会被提交到 io_context 的任务队列中。理论上,这两个定时器的到期事件可能被分配到不同的线程处理。但由于绑定了 strand_,所有回调会被强制序列化执行,​实际表现为单线程顺序处理​。

  1. Strand 的核心作用
    ​线程安全与顺序保证
    strand_ Boost.Asio 提供的一种隐式同步机制,其作用包括:

​序列化回调执行:所有通过 strand_ 提交的回调(如 print1print2)会按提交顺序依次执行,即使多个线程在运行 io_context
​消除数据竞争:对共享资源 count_ 的修改(如++count_)会被限制在同一个线程上下文中,无需显式加锁。
​代码中的具体实现

timer1_.async_wait(boost::asio::bind_executor(strand_, ...));

将定时器的回调函数绑定到 strand_ 的执行器上,确保回调在 strand_ 的调度下运行。因此,即使两个定时器的到期事件被不同线程触发,print1print2 也不会并发执行。

  1. 执行流程示例
    假设两个定时器同时到期:
    1. ​事件触发:timer1_timer2_ 的到期事件被提交到 io_context
    2. ​线程分配:事件可能被主线程或工作线程 t 处理。
    3. ​Strand 调度:无论哪个线程处理事件,回调函数会被 strand_ 序列化。例如:
      • 线程1执行 print1 → 输出 Timer 1: 0
      • 线程2执行 print2 → 但需等待 print1 完成后才能执行,输出 Timer 2: 1
      • 最终 count_ 会严格递增至10,无并发问题

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 函数连接到服务端,然后通过 sendrecv 函数进行数据的发送和接收,最后通过 close 函数关闭连接。

同步TCP时间服务器

  1. 命令行参数
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
  1. 同步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;
}
  1. 同步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;
}
  1. boost::asio::io_context io_context;

​作用: 创建 I/O 上下文对象,管理所有底层 I/O 操作(如网络通信、定时器等)。
​原理:

  • io_contextBoost.Asio 的 ​事件循环核心,负责调度和处理异步操作。
    所有异步操作(如连接、读写)都需要通过它执行。
  1. tcp::resolver resolver(io_context);

​作用: 创建解析器对象,用于将 ​主机名 和 ​服务名/端口号 转换为具体的网络地址(IP + 端口)。
​参数:

  • io_context:绑定到同一个 I/O 上下文,确保解析操作由该上下文管理。
    ​功能:
  • 解析器 (resolver) 支持 DNS 查询,将域名(如 "example.com")解析为 IP 地址。
  • 将服务名(如 "http")解析为端口号(如 80)。
  1. tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "cc");

​作用: 解析用户输入的主机名和服务名,生成可能的网络端点列表。
​参数:

  • argv[1]:命令行输入的主机名(如 "localhost""192.168.1.100")。
  • "cc":服务名或端口号。这里可能是自定义的服务名(需系统配置)或直接表示端口号(如 "12345")。
    ​返回值:
  • endpoints 是一个端点列表,包含所有可能的 IP 地址和端口组合(例如 IPv4IPv6 地址)。
  1. tcp::socket socket(io_context);

​作用: 创建 TCP 套接字对象,用于与服务器通信。
​参数:

  • io_context:套接字绑定到同一个 I/O 上下文,确保其操作由该上下文管理。
    ​功能:
  • 套接字是网络通信的端点,通过它可以发送和接收数据。
  1. boost::asio::connect(socket, endpoints);

​作用: 尝试连接到服务器。
​参数:

  • socket:上一步创建的套接字。
  • endpoints:解析得到的端点列表。
    ​行为:
  • 按顺序尝试连接 endpoints 中的每一个端点(如 IPv4 地址优先,失败后尝试 IPv6)。
  • 当某个端点连接成功时,停止尝试。
  • 如果所有端点均连接失败,抛出异常。

异步TCP时间服务器

  1. 主函数入口
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_contextBoost.Asio的核心,负责调度异步任务。
  • tcp_server 初始化时会开始监听连接。
  • io_context.run() 会阻塞主线程,持续处理异步事件,直到所有任务完成。
  1. ​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() 启动异步接受连接的过程。
  1. 异步接受连接(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 创建新的连接对象,其socketio_context管理。
  • async_accept 异步等待客户端连接。当有连接到来时,lambda被调用,传递错误码和连接对象。
  1. 处理新连接(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(),形成循环监听。
  1. 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
  1. 数据发送完成处理(handle_write)​
void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/)
{
  // 此处可添加发送完成后的逻辑,例如关闭连接
}
  • 当前代码未处理发送结果,但可在此处添加错误处理或资源释放逻辑。
  1. 生成时间字符串(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_acceptasync_writehandle_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;
}