• Boost ASIO:Network programming


    Abstract

    Even though Boost.Asio can process any kind of data asynchronously, it is mainly used for network programming. This is because Boost.Asio supported network functions long before additional I/O objects were added. Network functions are a perfect use for asynchronous operations because the transmission of data over a network may take a long time, which means acknowledgments and errors may not be available as fast as the functions that send or receive data can execute.

    尽管Asio可以异步处理任何类型的数据,但是主要用于网络编程。这是因为Boost早在添加额外的I/O对象之前,Asio就已经支持网络功能了。网络函数是异步操作的完美应用,因为通过网络传输数据可能需要很长时间,这意味着确认和错误可能没有发送或接收数据的函数执行得快。

    Demo

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace boost::asio;
    using namespace boost::asio::ip;
    
    io_service ioservice;
    tcp::resolver resolv{ioservice};
    tcp::socket tcp_socket{ioservice};
    std::array bytes;
    
    void read_handler(const boost::system::error_code &ec,
      std::size_t bytes_transferred)
    {
      if (!ec)
      {
        std::cout.write(bytes.data(), bytes_transferred);
        tcp_socket.async_read_some(buffer(bytes), read_handler);
      }
    }
    
    void connect_handler(const boost::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);
      }
    }
    
    void resolve_handler(const boost::system::error_code &ec,
      tcp::resolver::iterator it)
    {
      if (!ec)
        tcp_socket.async_connect(*it, connect_handler);
    }
    
    int main()
    {
      tcp::resolver::query q{"theboostcpplibraries.com", "80"};
      resolv.async_resolve(q, resolve_handler);
      ioservice.run();
    }


    在main()中,boost::asio::ip::tcp::resolver::query实例化创建一个q对象。q表示对名称解析器的查询,这是一个boost::asio::ip::tcp::resolver类型的I/O对象。通过将q传递给async_resolve(),将启动一个异步操作来解析名称。解析名称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参数包含已经接收到的字节数。通常,处理程序应该首先检查异步操作是否成功完成。只有在这种情况下,才会将数据写入标准输出。

    请注意,read_handler()在数据写入std::cout之后再次调用async_read_some()。这是必需的,因为您不能确定在单个异步操作中下载了整个主页并将其复制到字节中。重复调用async_read_some(),然后重复调用read_handler(),只有当连接关闭时才结束,这发生在web服务器已经发送了整个主页。然后read_handler()报告ec中的错误。此时,没有进一步的数据写入std::cout,并且不会在套接字上调用async_read()。因为没有挂起的异步操作,所以程序退出。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    using namespace boost::asio;
    using namespace boost::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;
    
    void write_handler(const boost::system::error_code &ec,
      std::size_t bytes_transferred)
    {
      if (!ec)
        tcp_socket.shutdown(tcp::socket::shutdown_send);
    }
    
    void accept_handler(const boost::system::error_code &ec)
    {
      if (!ec)
      {
        std::time_t now = std::time(nullptr);
        data = std::ctime(&now);
        async_write(tcp_socket, buffer(data), write_handler);
      }
    }
    
    int main()
    {
      tcp_acceptor.listen();
      tcp_acceptor.async_accept(tcp_socket, accept_handler);
      ioservice.run();
    }

    这是一个时间服务器。您可以与telnet客户端连接以获得当前时间。之后,时间服务器关闭。

    tcp::v4() 可以替换为address_type::from_string("127.0.0.1")。

    时间服务器使用I/O对象boost::asio::ip::tcp::acceptor来接收来自另一个程序的连接。您必须初始化对象,以便它知道在哪个端口上使用哪个协议。在这个例子中,类型为boost::asio::ip::tcp::endpoint的变量tcp_endpoint被用来告诉tcp_acceptor在端口2014上接受互联网协议版本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()。这个函数调用shutdown(),参数是boost::asio::ip::tcp::socket::shutdown_send,表示程序通过socket发送数据已经完成。由于没有等待的异步操作,示例32.6退出。请注意,尽管data仅用于accept_handler()中,但它不能是局部变量。数据通过boost::asio::buffer()传递给boost::asio::async_write()。当boost::asio::async_write()和accept_handler()返回时,异步操作已经启动,但还没有完成。在异步操作完成之前,数据必须存在。如果data是一个全局变量,这是可以保证的。

  • 相关阅读:
    新鲜出炉:appium2.0+ 单点触控和多点触控新的解决方案
    统一建模语言UML(1~8章在线测试参考答案)
    楼顶空地适合建造气膜体育馆吗?
    电子学会青少年软件编程 Python编程等级考试二级真题解析(选择题)2021年6月
    ELK日志采集系统
    谷氨酸三方突触丨SYSY谷氨酸GluA解决方案
    36_ue4进阶末日生存游戏开发[背包系统2]
    Hexo最新实战:(一)Hexo7.0+GitHub Pages博客搭建
    基于Bagging集成学习方法的情绪分类预测模型研究(文末送书)
    Web3中文|以太坊创始人V神:关注技术,而不是价格
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126596837