一、网络编程尽管 Boost.Asio 可以异步处理任何类型的数据但它主要用于网络编程。这是因为 Boost.Asio 早在添加额外的 I/O 对象之前就支持网络功能。网络函数非常适合异步操作因为通过网络传输数据可能需要很长时间这意味着确认和错误可能不会像发送或接收数据的函数那样快。二、库示例Boost.Asio 提供了许多 I/O 对象来开发网络程序。示例 32.5 使用类 boost::asio::ip::tcp::socket 与另一台计算机建立连接。此示例向网络服务器发送 HTTP 请求以下载主页。示例 32.5。带有 boost::asio::ip::tcp::socket 的网络客户端1234567891011121314151617181920212223242526272829303132333435363738394041424344#include boost/asio/io_service.hpp#include boost/asio/write.hpp#include boost/asio/buffer.hpp#include boost/asio/ip/tcp.hpp#include array#include string#include iostreamusingnamespaceboost::asio;usingnamespaceboost::asio::ip;io_service ioservice;tcp::resolver resolv{ioservice};tcp::socket tcp_socket{ioservice};std::arraychar, 4096 bytes;voidread_handler(constboost::system::error_code ec,std::size_tbytes_transferred){if(!ec){std::cout.write(bytes.data(), bytes_transferred);tcp_socket.async_read_some(buffer(bytes), read_handler);}}voidconnect_handler(constboost::system::error_code ec){if(!ec){std::string r GET / HTTP/1.1\r\nHost: theboostcpplibraries.com\r\n\r\n;write(tcp_socket, buffer(r));tcp_socket.async_read_some(buffer(bytes), read_handler);}}voidresolve_handler(constboost::system::error_code ec,tcp::resolver::iterator it){if(!ec)tcp_socket.async_connect(*it, connect_handler);}intmain(){tcp::resolver::query q{theboostcpplibraries.com,80};resolv.async_resolve(q, resolve_handler);ioservice.run();}Example32.5示例 32.5 使用了三个处理程序connect_handler() 和 read_handler() 在建立连接并接收到数据时被调用。 resolve_handler() 用于名称解析。因为只有在建立连接之后才能接收数据并且因为只有在解析名称之后才能建立连接所以各种异步操作都是在处理程序中启动的。在 resolve_handler() 中指向从名称解析的端点的迭代器 it 与 tcp_socket 一起用于建立连接。在 connect_handler() 中访问 tcp_socket 以发送 HTTP 请求并开始接收数据。由于所有操作都是异步的处理程序被传递给各自的函数。根据操作可能需要传递其他参数。例如迭代器它指的是从名称解析的端点。数组字节用于存储接收到的数据。在 main() 中boost::asio::ip::tcp::resolver::query 被实例化以创建对象 q。 q 表示对名称解析器的查询一个类型为 boost::asio::ip::tcp::resolver 的 I/O 对象。通过将 q 传递给 async_resolve()启动异步操作来解析名称。示例 32.5 解析名称 theboostcpplibraries.com。异步操作启动后在 I/O 服务对象上调用 run() 以将控制权传递给操作系统。解析名称后将调用 resolve_handler()。处理程序首先检查名称解析是否成功。在这种情况下ec 为 0。只有这样才能访问套接字以建立连接。要连接的服务器地址由第二个参数提供其类型为 boost::asio::ip::tcp::resolver::iterator。该参数是名称解析的结果。对 async_connect() 的调用之后是对处理程序 connect_handler() 的调用。再次首先检查 ec 以确定是否可以建立连接。如果是这样则在套接字上调用 async_read_some()。通过此调用开始读取数据。接收到的数据存储在数组字节中作为第一个参数传递给 async_read_some()。当接收到一个或多个字节并将其复制到字节时调用 read_handler()。 std::size_t 类型的参数 bytes_transferred 包含已接收的字节数。像往常一样处理程序应该首先检查异步操作是否成功完成。只有在这种情况下才会将数据写入标准输出。请注意在将数据写入 std::cout 后read_handler() 会再次调用 async_read_some()。这是必需的因为您无法确定整个主页是否已在单个异步操作中下载并复制到字节中。对 async_read_some() 的重复调用和对 read_handler() 的重复调用仅在连接关闭时结束这发生在网络服务器发送整个主页时。然后 read_handler() 在 ec 中报告错误。此时不会向 std::cout 写入更多数据并且不会在套接字上调用 async_read()。因为没有挂起的异步操作程序退出。示例 32.6。具有 boost::asio::ip::tcp::acceptor 的时间服务器12345678910111213141516171819202122232425262728293031323334#include boost/asio/io_service.hpp#include boost/asio/write.hpp#include boost/asio/buffer.hpp#include boost/asio/ip/tcp.hpp#include string#include ctimeusingnamespaceboost::asio;usingnamespaceboost::asio::ip;io_service ioservice;tcp::endpoint tcp_endpoint{tcp::v4(), 2014};tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};tcp::socket tcp_socket{ioservice};std::string data;voidwrite_handler(constboost::system::error_code ec,std::size_tbytes_transferred){if(!ec)tcp_socket.shutdown(tcp::socket::shutdown_send);}voidaccept_handler(constboost::system::error_code ec){if(!ec){std::time_tnow std::time(nullptr);data std::ctime(now);async_write(tcp_socket, buffer(data), write_handler);}}intmain(){tcp_acceptor.listen();tcp_acceptor.async_accept(tcp_socket, accept_handler);ioservice.run();}Example32.6示例 32.6 是一个时间服务器。您可以连接 telnet 客户端以获取当前时间。之后时间服务器关闭。时间服务器使用 I/O 对象 boost::asio::ip::tcp::acceptor 来接受来自另一个程序的传入连接。您必须初始化对象以便它知道在哪个端口上使用哪个协议。在示例中boost::asio::ip::tcp::endpoint 类型的变量 tcp_endpoint 用于告诉 tcp_acceptor 在端口 2014 上接受 Internet 协议版本 4 的传入连接。接收器初始化后调用listen() 使接收器开始监听。然后调用 async_accept() 以接受第一次连接尝试。必须将套接字作为第一个参数传递给 async_accept()该参数将用于在新连接上发送和接收数据。一旦另一个程序建立连接就会调用accept_handler()。如果连接建立成功当前时间会通过 boost::asio::async_write() 发送。此函数将 data 中的所有数据写入套接字。 boost::asio::ip::tcp::socket 还提供了成员函数 async_write_some()。此函数在至少发送一个字节时调用处理程序。然后处理程序必须检查发送了多少字节以及还需要发送多少字节。然后它必须再次调用 async_write_some()。使用 boost::asio::async_write() 可以避免重复计算要发送的字节数和调用 async_write_some()。使用此函数开始的异步操作仅在数据中的所有字节都发送完毕后才完成。发送数据后会调用 write_handler()。该函数使用参数 boost::asio::ip::tcp::socket::shutdown_send 调用shutdown()表示程序已完成通过套接字发送数据。由于没有待处理的异步操作示例 32.6 退出。请注意虽然 data 仅在 accept_handler() 中使用但它不能是局部变量。数据通过 boost::asio::buffer() 引用传递到 boost::asio::async_write()。当 boost::asio::async_write() 和 accept_handler() 返回时异步操作已开始但尚未完成。数据必须存在直到异步操作完成。如果数据是一个全局变量这是有保证的。练习开发可以将文件从一台计算机传输到另一台计算机的客户端和服务器。当服务器启动时它应该显示所有本地接口的 IP 地址列表并等待客户端连接。当客户端启动时来自服务器的 IP 地址和本地文件的名称应作为命令行选项传递。客户端应将文件传输到服务器服务器将其保存到当前工作目录。在传输期间客户端应该显示某种进度指示器以便用户知道传输正在进行中。使用回调实现客户端和服务器。