自学内容网 自学内容网

Boost.Asio学习(1):基础的客户端服务器

学习Asio之前,可以先学习Linux下的套接字编程,熟悉C风格的网络编程:

《Linux C编程实战》笔记:套接字编程-CSDN博客

本节先使用Asio建立最基本的服务器和客户端模型,并介绍基础的套接字api

服务器:

#include <boost/asio.hpp>
#include <iostream>
#include <array>

int main() {
try {
// 使用命名空间简化代码
namespace asio = boost::asio;
using tcp = asio::ip::tcp;

// 创建 io_context
asio::io_context io_context;

// 创建 TCP 监听器,绑定到 0.0.0.0:8080
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 8080));

std::cout << "Server is listening on port 8080..." << std::endl;

// 接收客户端连接
tcp::socket socket(io_context);
acceptor.accept(socket);
std::cout << "Client connected." << std::endl;

// 接收数据
std::array<char, 100> recv_buf{};
boost::system::error_code ec;
std::size_t len = socket.read_some(boost::asio::buffer(recv_buf), ec);

if (ec) {
std::cerr << "Receive failed: " << ec.message() << std::endl;
}
else {
std::cout << "Server received data: ";
std::cout.write(recv_buf.data(), len);
std::cout << std::endl;
}

// 自动析构时关闭 socket
}
catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
return 1;
}

return 0;
}

客户端:

#include <boost/asio.hpp>
#include <iostream>
#include <string>

int main() {
    try {
        namespace asio = boost::asio;
        using tcp = asio::ip::tcp;

        // 创建 IO 上下文对象
        asio::io_context io_context;

        // 创建 socket
        tcp::socket socket(io_context);

        // 构造服务器端地址(127.0.0.1:8080)
        tcp::endpoint endpoint(asio::ip::make_address("127.0.0.1"), 8080);

        // 连接服务器
        socket.connect(endpoint);

        // 准备要发送的数据
        std::string msg = "hello";  // 不包含 \0,Asio 不自动加
        msg.push_back('\0');        // 明确加上 \0(等价于你的 send_len = strlen+1)

        // 发送数据
        boost::system::error_code ec;
        std::size_t len = asio::write(socket, asio::buffer(msg), ec);

        if (ec) {
            std::cerr << "Send failed: " << ec.message() << std::endl;
        } else {
            std::cout << "Client sent " << len << " bytes." << std::endl;
        }

        // socket 自动析构关闭
    } catch (const std::exception& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

套接字socket

Boost.Asio 将 socket 抽象为一个类,比如常见的:

boost::asio::ip::tcp::socket
boost::asio::ip::udp::socket
// 基础构造,创建一个未绑定的 socket,必须传入 io_context
explicit socket(boost::asio::io_context& io_context);

还有一些指定协议,端口等的构造方式,不过很少需要这么原始地进行构造,一般就传递io_context即可。

与 Linux 原生 socket 的对比

功能 / 差异点Linux 原生 socketBoost.Asio socket
创建方式socket(AF_INET, SOCK_STREAM, 0)tcp::socket(io_context)
类型安全返回 int 文件描述符返回强类型类对象,类型安全
错误处理手动检查 errno使用 boost::system::error_code 或异常
自动关闭需要 close(fd)析构自动关闭资源
支持异步手动 epoll/select内置异步支持,如 async_read, async_write
跨平台仅 POSIX可同时用于 Windows、Linux、MacOS
支持 C++ RAII
多协议支持需要组合 AF_INET, SOCK_STREAM 等支持面向对象封装(如 tcp::socket, udp::socket
std::thread/协程集成不易易于与 co_await / asio::spawn 集成

成员函数--客户端连接

void connect(const endpoint_type& peer_endpoint);
void connect(const endpoint_type& peer_endpoint, boost::system::error_code& ec);
  • 作用:将该 socket 连接到一个指定的远程地址。

  • 只能连接一个 endpoint

  • tcp::socket 类的成员函数。

boost::asio::connect(...)(全局函数模板) 

  • 作用:尝试依次连接多个 endpoint(如 DNS 解析得到的多个 IP),直到成功;

  • 通常配合 resolver 一起使用;

  • 是 Boost.Asio 提供的通用连接封装。

boost::asio::ip::tcp::resolver resolver(io_context);
auto results = resolver.resolve("example.com", "http"); // 可能有多个 IP

boost::asio::ip::tcp::socket socket(io_context);
boost::asio::connect(socket, results); // 会尝试所有 IP,直到连上

endpoint

在 Boost.Asio 中,endpoint 是一个 抽象的网络地址和端口组合,相当于 Linux 中的 sockaddr_in 结构体,是网络通信的“地址单元”。

boost::asio::ip::tcp::endpoint
// 构造函数原型
tcp::endpoint(boost::asio::ip::address addr, unsigned short port);

创建方式:

using boost::asio::ip::tcp;
tcp::endpoint ep(boost::asio::ip::make_address("127.0.0.1"), 8080);
tcp::endpoint ep(tcp::v4(), 8080); // 任意IPv4地址 + 端口8080,类似 INADDR_ANY

常用成员函数 

函数名说明
address()返回 IP 地址,类型为 boost::asio::ip::address
port()返回端口号
protocol()返回协议类型,如 tcp::v4()tcp::v6()
address(a)设置 IP 地址
port(p)设置端口号

make_address

boost::asio::ip::make_address() 是 Boost.Asio 提供的一个 统一的 IP 地址解析函数,用于将字符串形式的 IP 地址(如 "127.0.0.1""::1")转成 boost::asio::ip::address 类型,以便用于创建 endpoint 等对象。 

// 异常版本(推荐)
boost::asio::ip::address make_address(const std::string& str);

// 不抛异常,使用 error_code 返回错误
boost::asio::ip::address make_address(const std::string& str, boost::system::error_code& ec);

用错误码版本:

boost::system::error_code ec;
address addr = make_address("invalid-ip", ec);
if (ec) {
    std::cerr << "Invalid address: " << ec.message() << std::endl;
}

acceptor ★★★

tcp::acceptor 是 Boost.Asio 中用于 TCP 服务器端监听和接收连接的类,它封装了底层的:

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(socket_fd, ...);
listen(socket_fd, backlog);

 等价于原生 socket 编程中服务器的准备阶段。

构造函数

tcp::acceptor(boost::asio::io_context& io_context);
tcp::acceptor(boost::asio::io_context& io_context, const endpoint_type& endpoint);
tcp::acceptor(boost::asio::io_context& io_context, const protocol_type& protocol, const endpoint_type& endpoint);

常见构造写法:

boost::asio::io_context io_context;
boost::asio::ip::tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 8080));

这个构造函数做了以下几件事:

  1. 创建 socket。

  2. 绑定到本地地址(这里是 0.0.0.0:8080)。

  3. 设置为监听状态,准备接受客户端连接。

accept

boost::asio::io_context io_context;

// 1. 创建 acceptor,并绑定 8080 端口(监听所有 IPv4 地址)
boost::asio::ip::tcp::acceptor acceptor(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 8080));

std::cout << "Server is listening on port 8080..." << std::endl;

// 2. 创建一个 socket 来接收客户端连接
boost::asio::ip::tcp::socket socket(io_context);

// 3. 同步阻塞,等待客户端连接到来(如同 accept())
acceptor.accept(socket);

// 4. 客户端连接建立后,socket 可用于 read/write
std::cout << "Client connected from: " << socket.remote_endpoint() << std::endl;

acceptor 的常用成员函数

成员函数说明
accept(tcp::socket& socket)同步阻塞,接收一个连接
async_accept(socket, handler)异步接收连接,回调通知
is_open()判断是否打开
close()关闭监听器
local_endpoint()获取本地监听地址信息
set_option() / get_option()设置/获取 socket 选项
native_handle()获取底层 socket fd
listen()显式调用 listen(通常构造函数已隐含)
bind(endpoint)显式调用 bind(通常构造函数已隐含)
cancel()取消所有挂起的异步操作

同步与异步的对比

✅ 同步接收连接(阻塞):

tcp::socket socket(io_context); acceptor.accept(socket);

调用线程会阻塞,直到某个客户端连接进来。

✅ 异步接收连接(非阻塞):

acceptor.async_accept(socket, [](const boost::system::error_code& ec) {
 if (!ec) { 
std::cout << "Client connected!" << std::endl; 
} 
});

调用立即返回,回调函数在连接建立时触发,适用于并发服务器。

 与原生 Linux socket 的对比

功能Linux Socket APIBoost.Asio
创建监听socket() + bind() + listen()构造 tcp::acceptor(endpoint)
接收连接accept()acceptor.accept(socket)
非阻塞接收accept() + select() / epoll()async_accept(socket, handler)
设置选项setsockopt()set_option()
获取绑定地址getsockname()local_endpoint()

原文地址:https://blog.csdn.net/ouliten/article/details/149093592

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!