Boost.Asio学习(1):基础的客户端服务器
学习Asio之前,可以先学习Linux下的套接字编程,熟悉C风格的网络编程:
本节先使用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 原生 socket | Boost.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));
这个构造函数做了以下几件事:
-
创建 socket。
-
绑定到本地地址(这里是
0.0.0.0:8080
)。 -
设置为监听状态,准备接受客户端连接。
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 API | Boost.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)!