版权声明:所有的博客都是个人笔记,交流可以留言。未经允许,谢绝转载。。。 https://blog.csdn.net/qq_35976351/article/details/90113497
简介
这篇笔记是boost::asio的概览, 主要说明了boost
的进行CS结构编程的基本步骤. 在网络编程中, 又很多需要IO的操作. 一种是使用Linux的原生C语言API, Linux的核心编程思想是操作文件描述符, 所有的操作都是基于文件描述符. 而在boost中, 增添了更多功能的操作, 而且更加增强了关于异步的操作. 这篇笔记主要记录了使用boost::asio库进行的操作.
客户端
客户端主要的操作步骤是:
- 建立
io_service
, 这是程序和内核的IO连接, 需要使用boost::asio::io_context
来建立. 本例子中, 使用:boost::asio::io_context io_context;
- 需要一个解析器解析服务器的信息, 比如IP和端口,
boost::asio::ip::tcp::resolver
进行解析, 在本例子中:tcp::resolver resolver(io_context);
- 需要一个终端抽象, 来存储解析器解析的信息, 在本例子中:
boost::asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "6768"); // ip和6768端口
- 之后, 需要一个
socket
建立连接, 使用:boost::asio::ip::tcp::socket socket(io_context);
- 建立与服务器的链接, 是
socket
与endpoints
的连接:boost::asio::connect(socket, endpoints);
- 最后是通过有关的读写函数进行操作, 具体参考代码
代码实例, 参考自: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/tutorial/tutdaytime1/src.html
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
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; // 与内核连接的上下文
tcp::resolver resolver(io_context); // 解析器, 需要一个上下文
// tcp::resolver::results_type endpoints =
// resolver.resolve(argv[1], "6768");
// 解析存储的信息, 建议使用auto自动推导, 上下两个等价
auto endpoints = resolver.resolve(argv[1], "6768");
tcp::socket socket(io_context); // 与上下文相关的套接字
boost::asio::connect(socket, endpoints); // 建立连接
for (;;) {
boost::array<char, 128> buf;
boost::system::error_code error;
// 数据的缓冲区, 参考: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/buffer.html
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof) {
std::cout << "closed by server\n";
break; // 服务器主动关闭连接.
}
else if (error) {
throw boost::system::system_error(error); // 其他的错误.
std::cout << "error: ";
}
std::cout.write(buf.data(), len); // 控制台输出
}
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
同步服务器
服务编程的主要步骤是:
- 建立上下文与内核进行连接, 与客户端一样:
boost::asio::io_context io_context;
- 需要有一个接收器, 用于在指定端口接收数据, 并通过指定的套接字进行读写:
using boost::asio::ip::tcp; // ipv4地址, 并监听6768端口 tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 6768));
- 建立套接字, 用于数据传输
tcp::socket socket(io_context); acceptor.accept(socket);
- 通过有关函数, 写入或者读取数据
代码实例, 参考: https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/tutorial/tutdaytime2/src.html
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
std::string make_daytime_string() {
using namespace std;
time_t now = time(0);
return ctime(&now);
}
int main() {
try {
boost::asio::io_context io_context;
// 接收器, 初始化指定上下文 ip和端口
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 6768));
for (;;) {
tcp::socket socket(io_context); // 建立socket
acceptor.accept(socket); // 用于读写数据的socket
std::string message = make_daytime_string();
boost::system::error_code ignored_error;
// 向socket写入数据
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
异步服务器
异步服务器的作用在于, 读写操作会立刻返回, 而不是在函数的调用时阻塞.
#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
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 boost::enable_shared_from_this<tcp_connection> {
public:
using pointer = boost::shared_ptr<tcp_connection>;
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_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
explicit tcp_connection(boost::asio::io_context &io_context)
: socket_(io_context) {
}
void handle_write(const boost::system::error_code &, size_t) {
// 该函数一般是用户自己实现, 在异步写完后, 会调用该函数
}
tcp::socket socket_;
std::string message_;
};
class tcp_server {
public:
explicit tcp_server(boost::asio::io_context &io_context)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), 6768)) {
start_accept();
}
private:
void start_accept() {
auto new_connection =
tcp_connection::create(acceptor_.get_executor().context());
// 该函数调用后, 立刻返回, 建立连接完成后, 会调用handle_accept函数
acceptor_.async_accept(new_connection->socket(),
boost::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();
}
tcp::acceptor acceptor_;
};
int main() {
try {
boost::asio::io_context io_context;
tcp_server server(io_context);
io_context.run();
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
return 0;
}