作者:陌城花开2010 | 来源:互联网 | 2024-10-29 11:10
使用Boost.Asio进行异步数据处理的应用程序主要依赖于两个核心概念:I/O服务和I/O对象。I/O服务抽象了操作系统接口,使得异步操作能够高效地执行。I/O对象则代表了具体的网络资源,如套接字和文件描述符,通过这些对象可以实现数据的读写操作。本文详细介绍了这两个概念在Boost.Asio中的应用及其在网络编程中的重要性。
I/O 服务与I/O 对象
使用Boost的Asio进行异步处理数据的应用程序基于两个概念:
I/O服务抽象了操作系统的接口,允许第一时间进行异步数据处理,而I/O对象则初始化特定的操作。
// I/O服务
boost::asio:io_service// 用于网络发送和接收数据
boost::asio::ip::tcp::socket// 计时器
boost::asio::deadline_timer
#include
#includevoid handler1(const boost::system::error_code &ec)
{std::cout <<"5 s." <}void handler2(const boost::system::error_code &ec)
{std::cout <<"10 s." <}int main()
{//1 初始化 I/O服务boost::asio::io_service io_service;//2 初始化I/O对象timer,使用boost::posix_time进行时间处理boost::asio::deadline_timer timer1(io_service, boost::posix_time::seconds(5));//3 该函数调用会立即返回timer1.async_wait(handler1);boost::asio::deadline_timer timer2(io_service, boost::posix_time::seconds(10));timer2.async_wait(handler2);//4 启动异步处理&#xff0c;进入阻塞状态io_service.run();return 0;
}
可扩展性与多线程
用boost asio这样的库开发程序&#xff0c;与一般的C&#43;&#43;风格不同。哪些可能需要较长时间才返回的函数不再以顺序的方式来调用。不在是调用阻塞式的函数&#xff0c;boost asio是启动一个异步操作。而那些需要在操作结束后调用的函数则实现为相应的句柄。这种方法的缺点是&#xff0c;本来顺序执行的功能变得在物理上分割开来了&#xff0c;从而令相应的代码更难理解。
象 Boost asio 这样的库通常是为了令应用程序具有更高的效率。应用程序不在需要等待特定的函数执行完成&#xff0c;而可以在期间执行其它任务&#xff0c;如开始另一个需要较长时间的操作。
#include
#include
#includevoid handler1(const boost::system::error_code &ec)
{std::cout <<"5 s." <}void handler2(const boost::system::error_code &ec)
{std::cout <<"5 s." <}// 使用两个 I/O 服务
boost::asio::io_service io_service1;
boost::asio::io_service io_service2;void run1()
{io_service1.run();
}void run2()
{io_service2.run();
}int main()
{boost::asio::deadline_timer timer1(io_service1, boost::posix_time::seconds(5));timer1.async_wait(handler1);boost::asio::deadline_timer timer2(io_service2, boost::posix_time::seconds(5));timer2.async_wait(handler2);boost::thread thread1(run1);boost::thread thread2(run2);thread1.join();thread2.join();
}
这个应用程序的功能与前一个相同。在一定条件下使用多个I/O服务是有好处的&#xff0c;每个I/O服务有自己的线程&#xff0c;最好是运行在各自的处理器内核上&#xff0c;这样每一个异步操作连同它们的句柄就可以局部化执行。如果没有远端的数据或函数需要访问&#xff0c;那么每一个I/O服务就像一个小的自主应用。这里的局部和远端是指像高速缓存&#xff0c;内存页这样的资源。由于在确定优化策略之前需要对底层硬件&#xff0c;操作系统&#xff0c;编译器以及潜在的瓶颈有专门的了解&#xff0c;所以应该仅在清楚这些好处的情况下使用多个I/O服务。
网络编程
虽然 boost.asio 是一个可以异步处理任何种类数据的库&#xff0c;但是它主要被用于网络编程。这是由于&#xff0c;事实上Boost.Asio在加入其他I/O对象之前很久就已经支持网络功能了。网络功能是异步处理的一个很好的例子&#xff0c;因为通过网络进行数据传输可能会需要较长时间&#xff0c;从而不能直接获得确认或错误条件。
#include
#include
#include
#include boost::asio::io_service io_service;
// 域名解析器
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
boost::array buffer; void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{ if (!ec) { std::cout <} void connect_handler(const boost::system::error_code &ec)
{ if (!ec) { boost::asio::write(sock, boost::asio::buffer("GET / HTTP 1.1\r\nHost: graycatya.com\r\n\r\n")); sock.async_read_some(boost::asio::buffer(buffer), read_handler); }
} void resolve_handler(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it)
{ if (!ec) { sock.async_connect(*it, connect_handler);}
} int main()
{ boost::asio::ip::tcp::resolver::query query("www.graycatya.com", "80"); resolver.async_resolve(query, resolve_handler); io_service.run();
}
- boost::asio::ip::tcp::resolver
域名解析也是一个需要连接到互联网的过程。 有些专门的PC&#xff0c;被称为DNS服务器&#xff0c;其作用就象是电话本&#xff0c;它知晓哪个IP地址被赋给了哪台PC。 由于这个过程本身的透明的&#xff0c;只要明白其背后的概念以及为何需要。
一旦域名解析成功或被某个错误中断&#xff0c;resolve_handler() 函数就会被调用。
*** 软件执行流程 ***
- 应用创建一个类型(bost::asio::ip::tcp::resolver::query)对象query&#xff0c;表示一个查询&#xff0c;这个查询被传递给async_resolve()方法以解析该域名。
- 当域名解析完成后&#xff0c;resolve_handler()被调用 访问I/O对象sock,用由迭代器it所提供的解析后地址创建一个连接&#xff0c;如果解析成功&#xff0c;则存有错误条件的对象ec被设为0&#xff0c;只有在这种情况下&#xff0c;才会相应地访问socket以创建连接。服务器地地址是通过类型为boost::asio::ip::tcp::resolver::iterator提供。
- 调用了 async_connect()方法之后&#xff0c;connect_handler()会被自动调用。在该句柄地内部&#xff0c;会访问ec对象以检查连接是否已建立。如果连接是有效的&#xff0c;则对相应的socket调用async_read_some()方法&#xff0c;启动读数据操作。为保存接收到的数据&#xff0c;要提供一个缓冲区作为第一个参数。在以下例子中&#xff0c;缓冲区类型是boost::array。
- 每当有一个或多个字节被接收并保存至缓冲区时&#xff0c;read_handler()函数就会被调用。准确的字节数通过std::size_t 类型的参数 bytes_transferred 给出。同样的规则&#xff0c;该句柄应该首先看看参数ec以检查有没有接收错误。如果是成功接收&#xff0c;则将数据写出至标准输出流。
注意&#xff1a;read_handler() 在将数据写出至std::cout之后&#xff0c;会再次调用async_read_some()方法。这是必需的&#xff0c;因为无法保证仅在一次异步操作中就可以接收到整个网页。async_read_some() 和 read_handler() 的交替调用只有当连接被破坏时才中止&#xff0c;如当web服务器已经传送完整个网页时。这种情况下&#xff0c;在read_handler() 内部将报告一个错误&#xff0c;以防止进一步将数据输出至标准输出流&#xff0c;以及进一步对该socket调用async_read() 方法。