赞
踩
The Boost.Asio库是为使用C++进行系统编程的程序员设计的,在系统编程中,通常需要访问操作系统功能(如网络)。特别是Boost。Asio致力于实现以下目标:
便携性。该库应支持一系列常用的操作系统,并在这些操作系统之间提供一致的行为。
可扩展性。该库应促进可扩展到数千个并发连接的网络应用程序的开发。每个操作系统的库实现都应该使用最能实现这种可伸缩性的机制。
效率该库应支持分散采集I/O等技术,并允许程序最大限度地减少数据复制。
从已建立的API(如BSD套接字)中建模概念。BSD套接字API被广泛地实现和理解,并且在许多文献中都有涉及。其他编程语言通常使用类似的接口来连接API。在合理的范围内,Boost。Asio应该利用现有的做法。
易于使用。图书馆应采用工具包而非框架方法,为新用户提供较低的进入门槛。也就是说,它应该尽量减少前期投资,及时学习一些基本规则和指导方针。之后,库用户应该只需要了解正在使用的特定函数。
进一步抽象的基础。该库应允许开发提供更高抽象级别的其他库。例如,常用协议(如HTTP)的实现。
尽管Boost.Asio最初主要专注于网络,其异步I/O的概念已经扩展到包括其他操作系统资源,如串行端口、文件描述符等。
即: Boost.Asio是可实现异步IO的网络编程库,它封装了socket, bind, listen, epoll等操作
在Linux系统上,boost::asio::steady_timer 是通过timerfd_create 和 timerfd_settime 等系统调用函数实现的。这些函数允许应用程序创建和设置定时器,并且可以以稳定的时间间隔触发定时器事件。
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
// 只有一个线程
#include <boost/asio.hpp>
// 使用asio库必须建立至少一个io执行上下文对象,如io_context,thread_pool
// io执行上下文提供了对io功能的访问
boost::asio::io_contex io;
boost::asio::steady_timer t(io, boost::asio::chrono::second(5)); // 对象构造出来后计时器就已经执行了
/*若这里的执行时间较长,则wait不阻塞*/
t.wait(); // wait 至到计时器结束
// 只有一个线程
void print(const boost::system::error_code& e)
{}
boost::asio::io_contex io;
boost::asio::steady_timer t(io, boost:;asio::chrono::seconds(5));
// 该函数接受一个可调用对象,函数签名:void(const boost::system::error_code&)
t.async_wait(&print)
io.run(); // 阻塞,至到所有工作都处理完
// 只有一个线程
#include <functional> #include <boost/asio.hpp> void print(const boost::system::error_code&, boost::asio::steady_timer*, t, int *count) { if (*count < 5) { ++(*count); // expires_at设置到期时间点,expiry()返回到期的时间点 // expiry返回一个time_point t->expires_at(t->expiry() + boost::asio::chrono::seconds(1)); // 异步等待,当到期时注册的函数会被调用 t->async_wait(std::bind(print, boost::asio::placeholders::error, t, count)); } } 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, boost::asio::placeholders::error, &t, &count)); io.run()
std::bind为创建一个可调用对象,可更改该对象的函数签名
void fun(int a, int b, int c, int d) {}
auto bind_fun = std::bind(fun, a, b, std::placeholders::_1, d);
bind_fun(c);
std::_Placeholder<9> s;
auto b_fun = std::bind(fun, 1, s); // fun后面为传入函数的参数
b_fun(1,2,3,4,5,6,7,8,9); // 分别是placeholders::_1 _2 _3 ... _9
#include <functional> #include <iostream> #include <boost/asio.hpp> class printer { public: printer(boost::asio::io_context& io) : timer_(io, boost::asio::chrono::seconds(1)), count_(0) { timer_.async_wait(std::bind(&printer::print, this)); // 没有placeholders也是可以的 } ~printer() { std::cout << "Final count is " << count_ << std::endl; } void print() { if (count_ < 5) { std::cout << count_ << std::endl; ++count_; 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; }
注册的函数会在调用run的线程中执行,如果有多个线程同时调用了run呢?
// 多线程
如果有两个线程同时调用 run() 函数,每个线程都将从 io_context 的事件队列中获取事件并执行它们。由于您的 printer 类在构造函数中设置了两个定时器 t1_ 和 t2_,并且它们都是异步等待的,因此在每个定时器到期时,将触发相应的处理函数 print1() 和 print2()。
当两个线程同时调用 run() 时,它们将竞争 io_context 的事件队列,但由于 io_context 本身是线程安全的,因此它们不会相互干扰或导致竞态条件。每个线程将按照事件在队列中的顺序处理它们。
总体来说,两个线程将会以并发的方式执行 io_context 中的事件,但由于事件的触发和处理是按照事件队列的顺序进行的,因此您的打印机类的 print1() 和 print2() 函数也将按照预期的顺序执行。
#include <functional> #include <iostream> #include <thread> #include <boost/asio.hpp> using namespace std; namespace asio = boost::asio; class printer { private: // 提供线程安全的序列化执行,以确保异步操作在多线程环境中的顺序性 asio::strand<asio::io_context::executor_type> strand_; asio::steady_timer t1_; asio::steady_timer t2_; int count_; public: printer(asio::io_context& io) : strand_(asio::make_strand(io)), t1_(io, asio::chrono::seconds(1)), t2_(io, asio::chrono::seconds(1)), count_(0) { // strand_ 执行器与 print1 方法绑定在一起,这样就确保了 print1 方法在 strand_ 执行器上执行 t1_.async_wait(asio::bind_executor(strand_, bind(&printer::print1, this))); t2_.async_wait(asio::bind_executor(strand_, bind(&printer::print2, this))); } ~printer() { cout << "final count is " << count_ << endl; } void print1() { if (count_ < 10) { cout << "T1: " << count_ << " thid: " << this_thread::get_id() << endl; ++count_; t1_.expires_at(t1_.expiry() + asio::chrono::milliseconds(100)); t1_.async_wait(asio::bind_executor(strand_, bind(&printer::print1, this))); } else if (count_ < 1000) { cout << "T1: " << count_ << " thid: " << this_thread::get_id() << endl; ++count_; t1_.expires_at(t1_.expiry() + asio::chrono::milliseconds(200)); t1_.async_wait(asio::bind_executor(strand_, bind(&printer::print1, this))); } } void print2() { if (count_ < 1000) { cout << "T2: " << count_<< " thid: " << this_thread::get_id() << endl; ++count_; t2_.expires_at(t2_.expiry() + asio::chrono::milliseconds(100)); t2_.async_wait(asio::bind_executor(strand_, bind(&printer::print2, this))); } } }; int main() { asio::io_context io; printer p(io); thread t([&]{io.run();}); // 不调用run,注册函数就不会被执行 io.run(); t.join(); return 0; }
Daytime Protocol:
服务端无论接受到什么都返回当前日期
Weekday, Month Day, Year Time-Zone
Tuesday, February 22, 1982 17:37:43-PST
socket
connet
using boost::asio::ip::tcp; namespace asio = boost::asio; try { // 使用asio库必须建立至少一个io执行上下文对象 asio::io_context io_context; // 将xxx解析成xxx tcp::resolver resolver(io_context); tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "daytime"); // 创建socket,建立连接 tcp::socket socket(io_context); boost::asio::connect(socket, endpoints); for (;;) { std::array<char, 128> buf; boost::system::error_code error; // 阻塞读取数据 size_t len = socket.read_some(boost::asio::buffer(buf), error); if (error == boost::asio::error::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); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; }
socket
bind
listen
accept
boost::asio::io_context io_context;
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_context);
acceptor.accept(socket); // 阻塞,等待客户端连接
std::string message = make_daytime_string();
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
std::enable_shared_from_this 是一个 C++11 标准库提供的模板类,位于 <memory> 头文件中。它的作用是允许一个对象可以在 tcp_connection 的成员函数中调用 shared_from_this() 方法来获取指向当前对象的 std::shared_ptr
// 函数原型 template<typename AsyncWriteStream, typename ConstBufferSequence, typename WriteHandler> void async_write(AsyncWriteStream & s, const ConstBufferSequence & buffers, WriteHandler handler); io_context io; ip::tcp::socket socket(io); socket.connect(ip::tcp::endpoint(ip::address::from_string("127.0.0.1"), 8080)); std::string data = "Hello, world!"; async_write(socket, boost::asio::buffer(data), [](const boost::system::error_code& error, std::size_t bytes_transferred) { if (!error) { std::cout << "Data sent successfully." << std::endl; } else { std::cerr << "Error: " << error.message() << std::endl; } }); io.run();
//using namespace boost::asio; // asio is namespace, 将namespace 中的内容释放出来 //namespace asio = boost::asio; //using boost::asio::ip::tcp; // tcp is class #include <ctime> #include <functional> #include <iostream> #include <memory> #include <string> #include <boost/asio.hpp> namespace asio = boost::asio; using boost::asio::ip::tcp; class tcp_connection : public std::enable_shared_from_this<tcp_connection > { private: tcp_connection(asio::io_context& io) :socket_(io) {} void handle_write(const boost::system::error_code&, size_t) {} tcp::socket socket_; std::string message_; public: typedef std::shared_ptr<tcp_connection> pointer; static pointer create(asio::io_context& io) { return pointer(new tcp_connection(io)); } tcp::socket& socket() { return socket_; } void start(){ // message_ = get_time(); // 该函数保证整个数据块被发送 message_ = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"; asio::async_write( // 这里用shared_from_this而非 socket_, asio::buffer(message_), std::bind(&tcp_connection::handle_write, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred) ); } }; class tcp_server{ private: asio::io_context& io_; tcp::acceptor acceptor_; void start_accept() { tcp_connection::pointer new_conn = tcp_connection::create(io_); acceptor_.async_accept(new_conn->socket(), std::bind(&tcp_server::handle_accept, this, new_conn, asio::placeholders::error)); } void handle_accept(tcp_connection::pointer new_conn, const boost::system::error_code& error) { if (!error) { new_conn->start(); } start_accept(); } public: tcp_server(asio::io_context& io) :io_(io), acceptor_(io, tcp::endpoint(tcp::v4(), 9000)) { start_accept(); } }; int main() { try{ asio::io_context io; // 使用asio必须有io上下文对象 tcp_server server(io); io.run(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }
只有一个线程
执行流解析:
tcp_server server(io);->start_accept()->async_accept(注册异步接收事件,当事件发生是调用handle_accept)-> io.run()在此处阻塞。
当有事件发生时:从io.run的阻塞处跳转至handle_accept执行->start->async_write(异步写数据)-> start_accept(再次注册) -> 跳回到 io.run()在此处阻塞
#include <array> #include <iostream> #include <boost/asio.hpp> using namespace std; using boost::asio::ip::udp; namespace ip = boost::asio::ip; namespace asio = boost::asio; int main(int argc, char* argv[]) { try { asio::io_context io; // 通过host,services获取远程endpoint // 获取远程信息,如远程地址,端口, udp::resolver resolver(io); udp::endpoint receiver_endpoint = // 解引用是安全的,至少返回一个 *resolver.resolve(udp::v4(), argv[1], "daytime").begin(); ip::address::from_string("127.0.0.1"); // udp::socket socket(io); socket.open(udp::v4()); array<char, 1> send_buf{0}; socket.send_to(asio::buffer(send_buf), udp::endpoint(ip::address::from_string(argv[1]), 9000)); array<char, 128> recv_buf; udp::endpoint sender_endpoint; // 由receive_from初始化endpoint 获取服务器相关信息 size_t len = socket.receive_from(asio::buffer(recv_buf), sender_endpoint); cout.write(recv_buf.data(), len); // cout << recv_buf.data() << endl; } catch(const std::exception& e) { std::cerr << "WAHT?:" << e.what() << '\n'; } }
#include <array> #include <ctime> #include <iostream> #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); } int main() { try { boost::asio::io_context io_context; udp::socket socket(io_context, udp::endpoint(udp::v4(), 9000)); for (;;) { std::array<char, 1> recv_buf; udp::endpoint remote_endpoint; socket.receive_from(boost::asio::buffer(recv_buf), remote_endpoint); std::string message = "HTTP/1.1 200 OK\r\nContent-Type:text\r\n\r\n"; message += make_daytime_string(); boost::system::error_code ignored_error; socket.send_to(boost::asio::buffer(message), remote_endpoint, 0, ignored_error); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }
#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; }
#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(), 9000)) { 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; }
https://www.boost.org/doc/libs/1_84_0/doc/html/boost_asio.html
https://www.boost.org/doc/libs/1_84_0/doc/html/boost_asio/tutorial.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。