Boost 库教程!本篇是 C++ Boost 库教程(二):Boost.Asio 网络编程入门(TCP/UDP 服务器与客户端),旨在帮助您掌握 Boost.Asio 的基础网络编程,重点实现 TCP 和 UDP 服务器与客户端的开发。结合您之前的上下文(尝试编译 Boost 1.88.0 的 64 位调试版静态库 libboost_system-vc143-mt-gd-x64-1_88.lib 和 libboost_log-vc143-mt-gd-x64-1_88.lib 未成功,使用 Visual Studio 2022,目标为 64 位静态库,最新信息显示仍在 x86 编译器环境),本教程将假设您已解决编译问题(若未解决,我会在文末提供进一步排查步骤),并提供完整的代码示例、编译命令和测试方法,确保您能快速上手 Boost.Asio 开发 TCP/UDP 调试工具。内容以中文呈现,结构清晰,适合初学者到中高级开发者。


C++ Boost 库教程(二):Boost.Asio 网络编程入门(TCP/UDP 服务器与客户端)

0. 写在前面的话

Boost.Asio 是 Boost 库中最强大的组件之一,用于异步 I/O 操作,特别适合网络编程(如 TCP、UDP、SSL)以及低级 I/O(如文件、串口)。它提供了跨平台、高性能的网络通信解决方案,广泛应用于服务器开发、实时通信和调试工具。Boost.Asio 的设计基于 异步回调 和 现代 C++ 特性(如 C++20 协程),支持单线程和多线程模型,灵活且高效。

在本教程中,我们将:

  • 介绍 Boost.Asio 的核心概念。

  • 实现简单的 TCP 服务器与客户端,支持消息回显。

  • 实现简单的 UDP 服务器与客户端,支持广播消息。

  • 使用 Boost.Log 记录通信日志,方便调试。

  • 提供 Visual Studio 2022 的配置和编译步骤,针对 64 位调试版静态库。

  • 附带测试方法和常见问题解答。

结合您的问题(开发 TCP/UDP 调试工具,需 Boost.System 和 Boost.Log),本教程将特别优化代码以匹配您的需求,确保代码简洁且可运行。若您仍未生成所需的 Boost 库,我会在文末提供针对性解决方案。


1. Boost.Asio 简介

1.1 什么是 Boost.Asio?

Boost.Asio(Asynchronous Input/Output)是一个跨平台的 C++ 库,用于处理异步 I/O 操作,包括:

  • 网络通信:TCP、UDP、ICMP、WebSocket(通过 Boost.Beast)。

  • 低级 I/O:文件、管道、串口。

  • 定时器:定时任务和超时处理。

  • 信号处理:捕获 SIGINT 等系统信号。

Boost.Asio 的核心特点:

  • 异步模型:支持回调、协程(C++20)、Future/Promise。

  • 高性能:基于操作系统的异步 I/O(如 Windows 的 IOCP、Linux 的 epoll)。

  • 跨平台:支持 Windows、Linux、macOS。

  • 依赖性:依赖 Boost.System(提供错误码处理)。

1.2 核心概念

  1. io_context:Asio 的核心事件循环,管理所有异步操作。

  2. socket:表示网络连接(如 TCP 或 UDP 套接字)。

  3. acceptor:用于 TCP 服务器,监听并接受客户端连接。

  4. resolver:解析主机名和端口(如 localhost:8888)。

  5. buffer:管理读写数据的缓冲区(如 asio::buffer)。

  6. error_code:处理操作错误(来自 Boost.System)。

1.3 与您的问题相关

  • Boost.System:libboost_system-vc143-mt-gd-x64-1_88.lib 是 Boost.Asio 的依赖,用于错误处理。

  • Boost.Log:libboost_log-vc143-mt-gd-x64-1_88.lib 用于记录通信日志,适合您的调试工具。

  • 本教程的代码将使用这两个库,并提供 Visual Studio 2022 的 64 位调试版配置。


2. TCP 服务器与客户端实现

2.1 功能

  • TCP 服务器:监听端口 8888,接受客户端连接,接收消息并回显。

  • TCP 客户端:连接服务器,发送消息并接收回显。

  • 日志:使用 Boost.Log 记录连接、消息和错误。

2.2 TCP 服务器代码:tcp_server.cpp

cpp

#include <boost/asio.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <iostream>
#include <memory>
#include <string>

namespace asio = boost::asio;
using asio::ip::tcp;

// 初始化日志
void init_logging() {
    boost::log::add_file_log("tcp_server.log", boost::log::keywords::auto_flush = true);
    boost::log::add_common_attributes();
}

class TcpSession : public std::enable_shared_from_this<TcpSession> {
public:
    TcpSession(asio::io_context& io_context) : socket_(io_context) {}

    tcp::socket& socket() { return socket_; }

    void start() {
        socket_.async_read_some(asio::buffer(buffer_),
            [this, self = shared_from_this()](const boost::system::error_code& error, size_t bytes_transferred) {
                if (!error) {
                    std::string message(buffer_.data(), bytes_transferred);
                    BOOST_LOG_TRIVIAL(info) << "Received: " << message;
                    std::string response = "Echo: " + message;
                    socket_.async_write_some(asio::buffer(response),
                        [self](const boost::system::error_code& error, size_t) {
                            if (!error) {
                                self->start(); // 继续读取
                            } else {
                                BOOST_LOG_TRIVIAL(error) << "Write error: " << error.message();
                            }
                        });
                } else {
                    BOOST_LOG_TRIVIAL(error) << "Read error: " << error.message();
                }
            });
    }

private:
    tcp::socket socket_;
    std::array<char, 1024> buffer_;
};

class TcpServer {
public:
    TcpServer(asio::io_context& io_context, unsigned short port)
        : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {
        start_accept();
    }

private:
    void start_accept() {
        auto session = std::make_shared<TcpSession>(acceptor_.get_executor().context());
        acceptor_.async_accept(session->socket(),
            [this, session](const boost::system::error_code& error) {
                if (!error) {
                    BOOST_LOG_TRIVIAL(info) << "Accepted connection from " << session->socket().remote_endpoint();
                    session->start();
                } else {
                    BOOST_LOG_TRIVIAL(error) << "Accept error: " << error.message();
                }
                start_accept();
            });
    }

    tcp::acceptor acceptor_;
};

int main() {
    try {
        init_logging();
        asio::io_context io_context;
        TcpServer server(io_context, 8888);
        BOOST_LOG_TRIVIAL(info) << "TCP server started on port 8888";
        io_context.run();
    } catch (const std::exception& e) {
        BOOST_LOG_TRIVIAL(error) << "Exception: " << e.what();
    }
    return 0;
}

2.3 TCP 客户端代码:tcp_client.cpp

cpp

#include <boost/asio.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <iostream>
#include <string>

namespace asio = boost::asio;
using asio::ip::tcp;

void init_logging() {
    boost::log::add_file_log("tcp_client.log", boost::log::keywords::auto_flush = true);
    boost::log::add_common_attributes();
}

int main(int argc, char* argv[]) {
    try {
        init_logging();
        if (argc != 3) {
            std::cerr << "Usage: " << argv[0] << " <host> <port>\n";
            return 1;
        }

        asio::io_context io_context;
        tcp::resolver resolver(io_context);
        auto endpoints = resolver.resolve(argv[1], argv[2]);
        tcp::socket socket(io_context);
        asio::connect(socket, endpoints);

        BOOST_LOG_TRIVIAL(info) << "Connected to " << argv[1] << ":" << argv[2];

        std::string message = "Hello, Server!";
        asio::write(socket, asio::buffer(message));
        BOOST_LOG_TRIVIAL(info) << "Sent: " << message;

        std::array<char, 1024> buffer;
        boost::system::error_code error;
        size_t len = socket.read_some(asio::buffer(buffer), error);
        if (!error) {
            std::string response(buffer.data(), len);
            BOOST_LOG_TRIVIAL(info) << "Received: " << response;
        } else {
            BOOST_LOG_TRIVIAL(error) << "Read error: " << error.message();
        }
    } catch (const std::exception& e) {
        BOOST_LOG_TRIVIAL(error) << "Exception: " << e.what();
    }
    return 0;
}

2.4 编译与运行

  1. 编译环境:

    • 确保使用“VS 2022 的 x64 本机工具命令提示符”:

      bash

      cl
      • 输出:Microsoft (R) C/C++ Optimizing Compiler Version 19.43.34808 for x64。

    • 确保 Boost 1.88.0 已编译,C:\boost_1_88_0\stage\lib 包含:

      • libboost_system-vc143-mt-gd-x64-1_88.lib

      • libboost_log-vc143-mt-gd-x64-1_88.lib

      • libboost_thread-vc143-mt-gd-x64-1_88.lib

      • libboost_date_time-vc143-mt-gd-x64-1_88.lib

      • libboost_filesystem-vc143-mt-gd-x64-1_88.lib

      • libboost_regex-vc143-mt-gd-x64-1_88.lib

  2. 编译 TCP 服务器:

    bash

    cl tcp_server.cpp /I "C:\boost_1_88_0" /link /LIBPATH:"C:\boost_1_88_0\stage\lib" libboost_system-vc143-mt-gd-x64-1_88.lib libboost_log-vc143-mt-gd-x64-1_88.lib libboost_thread-vc143-mt-gd-x64-1_88.lib libboost_date_time-vc143-mt-gd-x64-1_88.lib libboost_filesystem-vc143-mt-gd-x64-1_88.lib libboost_regex-vc143-mt-gd-x64-1_88.lib /MTd
  3. 编译 TCP 客户端:

    bash

    cl tcp_client.cpp /I "C:\boost_1_88_0" /link /LIBPATH:"C:\boost_1_88_0\stage\lib" libboost_system-vc143-mt-gd-x64-1_88.lib libboost_log-vc143-mt-gd-x64-1_88.lib libboost_thread-vc143-mt-gd-x64-1_88.lib libboost_date_time-vc143-mt-gd-x64-1_88.lib libboost_filesystem-vc143-mt-gd-x64-1_88.lib libboost_regex-vc143-mt-gd-x64-1_88.lib /MTd
  4. 运行:

    • 启动服务器:

      bash

      .\tcp_server.exe
    • 启动客户端:

      bash

      .\tcp_client.exe 127.0.0.1 8888
  5. 测试:

    • 客户端发送“Hello, Server!”,服务器回显“Echo: Hello, Server!”。

    • 检查日志:

      • tcp_server.log:

        [2025-06-09 19:30:00.123] [info] TCP server started on port 8888
        [2025-06-09 19:30:05.456] [info] Accepted connection from 127.0.0.1:12345
        [2025-06-09 19:30:05.789] [info] Received: Hello, Server!
      • tcp_client.log:

        [2025-06-09 19:30:05.456] [info] Connected to 127.0.0.1:8888
        [2025-06-09 19:30:05.789] [info] Sent: Hello, Server!
        [2025-06-09 19:30:05.790] [info] Received: Echo: Hello, Server!

3. UDP 服务器与客户端实现

3.1 功能

  • UDP 服务器:监听端口 8889,接收消息并返回当前时间。

  • UDP 客户端:发送消息并接收服务器响应。

  • 日志:使用 Boost.Log 记录通信细节。

3.2 UDP 服务器代码:udp_server.cpp

cpp

#include <boost/asio.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <iostream>
#include <string>

namespace asio = boost::asio;
using asio::ip::udp;

void init_logging() {
    boost::log::add_file_log("udp_server.log", boost::log::keywords::auto_flush = true);
    boost::log::add_common_attributes();
}

class UdpServer {
public:
    UdpServer(asio::io_context& io_context, unsigned short port)
        : socket_(io_context, udp::endpoint(udp::v4(), port)) {
        socket_.set_option(asio::socket_base::broadcast(true));
        start_receive();
    }

private:
    void start_receive() {
        socket_.async_receive_from(asio::buffer(buffer_), remote_endpoint_,
            [this](const boost::system::error_code& error, size_t bytes_transferred) {
                if (!error) {
                    std::string message(buffer_.data(), bytes_transferred);
                    BOOST_LOG_TRIVIAL(info) << "Received from " << remote_endpoint_ << ": " << message;
                    std::string response = "Time: " + std::to_string(std::time(nullptr));
                    socket_.async_send_to(asio::buffer(response), remote_endpoint_,
                        [](const boost::system::error_code& error, size_t) {
                            if (error) {
                                BOOST_LOG_TRIVIAL(error) << "Send error: " << error.message();
                            }
                        });
                } else {
                    BOOST_LOG_TRIVIAL(error) << "Receive error: " << error.message();
                }
                start_receive();
            });
    }

    udp::socket socket_;
    udp::endpoint remote_endpoint_;
    std::array<char, 1024> buffer_;
};

int main() {
    try {
        init_logging();
        asio::io_context io_context;
        UdpServer server(io_context, 8889);
        BOOST_LOG_TRIVIAL(info) << "UDP server started on port 8889";
        io_context.run();
    } catch (const std::exception& e) {
        BOOST_LOG_TRIVIAL(error) << "Exception: " << e.what();
    }
    return 0;
}

3.3 UDP 客户端代码:udp_client.cpp

cpp

#include <boost/asio.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <iostream>
#include <string>

namespace asio = boost::asio;
using asio::ip::udp;

void init_logging() {
    boost::log::add_file_log("udp_client.log", boost::log::keywords::auto_flush = true);
    boost::log::add_common_attributes();
}

int main(int argc, char* argv[]) {
    try {
        init_logging();
        if (argc != 3) {
            std::cerr << "Usage: " << argv[0] << " <host> <port>\n";
            return 1;
        }

        asio::io_context io_context;
        udp::resolver resolver(io_context);
        auto endpoints = resolver.resolve(argv[1], argv[2]);
        udp::socket socket(io_context, udp::endpoint(udp::v4(), 0));

        std::string message = "Hello, UDP Server!";
        socket.send_to(asio::buffer(message), *endpoints.begin());
        BOOST_LOG_TRIVIAL(info) << "Sent to " << argv[1] << ":" << argv[2] << ": " << message;

        std::array<char, 1024> buffer;
        udp::endpoint sender_endpoint;
        size_t len = socket.receive_from(asio::buffer(buffer), sender_endpoint);
        std::string response(buffer.data(), len);
        BOOST_LOG_TRIVIAL(info) << "Received from " << sender_endpoint << ": " << response;
    } catch (const std::exception& e) {
        BOOST_LOG_TRIVIAL(error) << "Exception: " << e.what();
    }
    return 0;
}

3.4 编译与运行

  1. 编译 UDP 服务器:

    bash

    cl udp_server.cpp /I "C:\boost_1_88_0" /link /LIBPATH:"C:\boost_1_88_0\stage\lib" libboost_system-vc143-mt-gd-x64-1_88.lib libboost_log-vc143-mt-gd-x64-1_88.lib libboost_thread-vc143-mt-gd-x64-1_88.lib libboost_date_time-vc143-mt-gd-x64-1_88.lib libboost_filesystem-vc143-mt-gd-x64-1_88.lib libboost_regex-vc143-mt-gd-x64-1_88.lib /MTd
  2. 编译 UDP 客户端:

    bash

    cl udp_client.cpp /I "C:\boost_1_88_0" /link /LIBPATH:"C:\boost_1_88_0\stage\lib" libboost_system-vc143-mt-gd-x64-1_88.lib libboost_log-vc143-mt-gd-x64-1_88.lib libboost_thread-vc143-mt-gd-x64-1_88.lib libboost_date_time-vc143-mt-gd-x64-1_88.lib libboost_filesystem-vc143-mt-gd-x64-1_88.lib libboost_regex-vc143-mt-gd-x64-1_88.lib /MTd
  3. 运行:

    • 启动服务器:

      bash

      .\udp_server.exe
    • 启动客户端:

      bash

      .\udp_client.exe 127.0.0.1 8889
  4. 测试:

    • 客户端发送“Hello, UDP Server!”,服务器返回当前时间。

    • 检查日志:

      • udp_server.log:

        [2025-06-09 19:35:00.123] [info] UDP server started on port 8889
        [2025-06-09 19:35:05.456] [info] Received from 127.0.0.1:12345: Hello, UDP Server!
      • udp_client.log:

        [2025-06-09 19:35:05.456] [info] Sent to 127.0.0.1:8889: Hello, UDP Server!
        [2025-06-09 19:35:05.789] [info] Received from 127.0.0.1:8889: Time: 1623248105

4. Visual Studio 项目配置

若您更倾向于使用 Visual Studio IDE 开发,以下是项目配置步骤:

  1. 创建项目:

    • 在 Visual Studio 2022 创建“空项目(C++)”,命名为 AsioDemo。

    • 设置平台为 x64。

  2. 添加源文件:

    • 将 tcp_server.cpp、tcp_client.cpp、udp_server.cpp、udp_client.cpp 添加到项目。

  3. 设置包含路径:

    • 项目属性 > C/C++ > 常规 > 附加包含目录:

      C:\boost_1_88_0
  4. 设置库路径:

    • 项目属性 > 链接器 > 常规 > 附加库目录:

      C:\boost_1_88_0\stage\lib
  5. 设置运行时库:

    • 项目属性 > C/C++ > 代码生成 > 运行时库:

      • 调试模式:/MTd。

  6. 添加依赖库:

    • 项目属性 > 链接器 > 输入 > 附加依赖项:

      libboost_system-vc143-mt-gd-x64-1_88.lib
      libboost_log-vc143-mt-gd-x64-1_88.lib
      libboost_thread-vc143-mt-gd-x64-1_88.lib
      libboost_date_time-vc143-mt-gd-x64-1_88.lib
      libboost_filesystem-vc143-mt-gd-x64-1_88.lib
      libboost_regex-vc143-mt-gd-x64-1_88.lib
  7. 预处理器定义:

    • 项目属性 > C/C++ > 预处理器 > 预处理器定义:

      BOOST_LOG_DYN_LINK=0
      BOOST_ALL_NO_LIB=0
  8. 编译与运行:

    • 按 F5 运行,或在调试模式下运行服务器和客户端。


5. 常见问题与优化建议

5.1 常见问题

  1. 链接错误:无法解析的外部符号:

    • 确认 C:\boost_1_88_0\stage\lib 包含所有依赖库。

    • 确保使用 /MTd 运行时库,与 Boost 库一致。

  2. 连接失败:

    • 检查防火墙是否阻止端口 8888/8889:

      bash

      netsh advfirewall firewall add rule name="Asio Ports" dir=in action=allow protocol=TCP localport=8888
      netsh advfirewall firewall add rule name="Asio Ports UDP" dir=in action=allow protocol=UDP localport=8889
  3. 日志未生成:

    • 确保当前目录有写权限。

    • 检查 Boost.Log 配置是否正确。

5.2 优化建议

  1. 异步改进:

    • TCP 客户端可改为异步读写:

      cpp

      void async_read(tcp::socket& socket) {
          socket.async_read_some(asio::buffer(buffer_),
              [&socket](const boost::system::error_code& error, size_t len) {
                  if (!error) {
                      std::string response(buffer_.data(), len);
                      BOOST_LOG_TRIVIAL(info) << "Received: " << response;
                      async_read(socket);
                  }
              });
      }
  2. 多线程支持:

    • 为服务器添加多线程:

      cpp

      std::vector<std::thread> threads;
      for (int i = 0; i < std::thread::hardware_concurrency(); ++i) {
          threads.emplace_back([&io_context] { io_context.run(); });
      }
      io_context.run();
      for (auto& t : threads) {
          if (t.joinable()) t.join();
      }
  3. 超时处理:

    • 添加定时器检测客户端超时:

      cpp

      asio::steady_timer timer_(io_context, std::chrono::seconds(30));
      timer_.async_wait([](const boost::system::error_code& error) {
          if (!error) {
              BOOST_LOG_TRIVIAL(warning) << "Client timeout";
          }
      });

6. 若 Boost 库仍未编译成功的解决方法

您提到仍在 x86 编译器环境,且多次尝试未生成 libboost_system-vc143-mt-gd-x64-1_88.lib 和 libboost_log-vc143-mt-gd-x64-1_88.lib。以下是针对性排查步骤:

  1. 确认 x64 环境:

    • 运行:

      bash

      "C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
      cl
      • 确保输出为 for x64。若仍为 x86,检查 Visual Studio 安装:

        bash

        start "" "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe"
        • 确保“C++ 桌面开发”包含 x64 工具。

  2. 重新编译 Boost:

    • 清理:

      bash

      cd C:\boost_1_88_0
      rmdir /S /Q stage
      rmdir /S /Q bin.v2
    • 运行:

      bash

      bootstrap.bat
      .\b2.exe --toolset=msvc-14.3 address-model=64 architecture=x86 link=static threading=multi runtime-link=static variant=debug --with-system --with-log --with-thread --with-date_time --with-filesystem --with-regex stage > build_log.log 2>&1
    • 检查 C:\boost_1_88_0\stage\lib。

  3. 检查日志:

    • 打开 build_log.log,搜索 error 或 failed。

    • 常见问题:

      • C1083:头文件缺失,重新下载 Boost 1.88.0。

      • LINK error:缺少依赖,确认 --with-regex。

      • x86 环境:重新运行 vcvars64.bat。

  4. 使用 vcpkg 快速获取库(备选):

    • 安装 vcpkg:

      bash

      git clone https://github.com/microsoft/vcpkg
      cd vcpkg
      .\bootstrap-vcpkg.bat
    • 安装 Boost:

      bash

      .\vcpkg install boost-system:x64-windows-static boost-log:x64-windows-static
    • 集成到 Visual Studio:

      bash

      .\vcpkg integrate install

7. 总结

本教程介绍了 Boost.Asio 的网络编程基础,提供了 TCP 和 UDP 服务器与客户端的完整实现,结合 Boost.Log 记录通信日志,适合开发调试工具。代码使用 64 位调试版静态库,匹配您的需求。

后续教程预告:

  • 教程(三):Boost.Log 高级日志配置(异步日志、过滤器、格式化)。

  • 教程(四):Boost.Asio 进阶(SSL、协程、多线程、Boost.Beast)。

请提供以下信息以解决编译问题:

  • 运行 cl 的最新输出(确认 x64)。

  • build_log.log 的关键错误。

  • C:\boost_1_88_0\stage\lib 的文件列表。

  • Visual Studio 版本和安装路径。

祝您 Boost.Asio 开发顺利!

更多推荐