zoukankan      html  css  js  c++  java
  • boost asio(初学示例)

    Timer.1 - 使用同步定时器

    这个示例程序通过展示在定时器中执行一个阻塞等待来介绍Asio

    让我们从必须包含的头文件开始。

    所有的Asio类只要简单的包含"asio.hpp"头文件便可使用。

    #include <iostream>
    #include <boost/asio.hpp>

    因为本程序中使用了定时器,我们需要包含相应的的Boost.Date_Time 头文件来处理时间操作。

    #include <boost/date_time/posix_time/posix_time.hpp>

    使用Asio的所有程序都至少需要一个提供访问I/O功能的io_service 对象。因此在主函数中我们做的第一件事就是声明一个这个类型的对象。

    int main()
    {
      boost::asio::io_service io;

    接下来我们声明一个boost::asio::deadline_timer类型的对象。作为 Asio的核心类,它提供的I/O功能(在此为定时器功能)通常用一个io_service 的引用作为其构造函数的第一个参数。第二个参数设置一个从现在开始5秒后终止的定时器。

      boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

    在这个简单的程序中,我们用定时器演示一个阻塞等待。deadline_timer::wait() 函数调用直到定时器终止(从定时器被创建算起,五秒后终止)才会返回。

    一个deadline timer 通常是下面两种状态中的一种:"expired(终止)" "not expired(不终止)"。如果deadline_timer::wait() 函数被一个已经终止的定时器调用, 它将立即返回。

      t.wait();

    最后我们打印出 "Hello, world!" 信息以显示定时器已经终止。

      std::cout <<"Hello, world!\n";
    
      return0;
    }





    Timer.2 - 使用异步定时器

    这个示例程序示范了如何通过修改Timer.1 中的程序,使用Asio的异步回调功能在定时器中演示一个异步等待。

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>

    使用Asio的异步功能意味着当一个异步操作完成时一个回调函数将被调用。在本程序中我们定义一个名为print 的函数,在异步等待结束后这个函数将被调用。

    void print(const boost::system::error_code& /*e*/)
    {
      std::cout <<"Hello, world!\n";
    }
    
    int main()
    {
      boost::asio::io_service io;
    
      boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

    接下来,我们调用 deadline_timer::async_wait() 函数执行一个异步等待去取代Timer.1例中的阻塞等待。当调用这个函数时我们传入上面定义的print回调句柄。

      t.async_wait(print);

    最后,我们必须在io_service对象上调用io_service::run() 成员函数。

    Asio保证回调句柄仅仅能被io_service::run()启动的当前线程所调用。 因此,如果io_service::run() 函数不执行,用于异步等待完成时的回调函数(在本例中为print函数)将永远不会被调用。

    当仍旧有“工作”可做时,io_service::run() 函数会继续运行。在本例中,“工作”是定时器的异步等待,因此,直到定时器终止和回调函数执行完成,程序才会返回。

    在调用io_service::run()之前确保给 io_service 一些工作去做,这非常重要。 例如,如果我们省略了上面调用的deadline_timer::async_wait() 函数,io_service对象将没有任何事情去做,因此io_service::run() 将立即返回。

      io.run();
    
      return0;
    }
    
    

    Timer.3 - 回调函数绑定参数

    在本示例程序中我们将修改Timer.2中的例子,使定时器每秒被激活一次。例子将示范如何给你的函数指针传递附加参数。

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>

    使用Asio实现一个重复定时器,你必须在你的回调函数中去改变定时器的终止时间,然后开始一个新的异步等待。显然这意味着回调函数必须拥有改变定时器对象的权限。为此我们为 print函数增加两个新参数:

    • 一个指向定时器对象的指针。

    • 一个用于当定时器第6次被激活时我们可以中止程序的计数器。

    void print(const boost::system::error_code& /*e*/,
        boost::asio::deadline_timer* t,int* count)
    {

    如上所示,示例程序使用了一个计数器,当定时器被第6次激活时,用来中止程序。然而,你将看到这里并没有显式地要求io_service对象中止。回忆示例2中,当没有更多“工作”去做时,io_service::run() 函数完成。在计数器达到5时,定时器并没有启动一个新的异步等待。该io_service执行完工作后停止运行。

      if(*count <5)
      {
        std::cout <<*count <<"\n";
        ++(*count);

    接着,我们推迟定时器的终止时间。通过在原先的终止时间上增加延时,我们可以确保定时器不会在处理回调函数所需时间内到期。

        t->expires_at(t->expires_at()+ boost::posix_time::seconds(1));

    接着我们在定时器中启动一个新的异步等待。我们必须使用boost::bind() 函数给你的回调函数绑定额外的参数,因为deadline_timer::async_wait() 函数只期望得到一个拥用 void(const boost::system::error_code&)签名的函数指针(或函数对象)。为你的print函数绑定附加的参数后,它就成为与签名精确匹配的函数对象。

    查看Boost.Bind 文档文档以获得更多如何使用boost::bind()的信息。

    在本例中,boost::bind()的boost::asio::placeholders::error参数是为了给回调函数传入一个error对象。当开始异步操作时,如果使用boost::bind(),你必须指定和回调函数的参数列表相匹配的一个参数。在示例4中,如果在回调函数中,这个参数不是必需的,这个占位符会被省略。

        t->async_wait(boost::bind(print,
              boost::asio::placeholders::error, t, count));
      }
    }
    
    int main()
    {
      boost::asio::io_service io;

    为了在定时器第6次被激活时终止程序,我们添加一个新的count变量。

      int count =0;
      boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));

    在第四步中,当在主函数中的调用deadline_timer::async_wait() 函数时,我们绑定print函数所需要的附加参数。

      t.async_wait(boost::bind(print,
            boost::asio::placeholders::error,&t,&count));
    
      io.run();

    最后,为了证明count 变量在print 函数句柄中被使用,我们打印出它的值。

      std::cout <<"Final count is "<< count <<"\n";
    
      return0;
    }









    Timer.5 - 多线程同步回调

    本示例程序示范了使用boost::asio::strand 类来创建多线程程序中的同步回调句柄。

    前四个例程只是在单线程下使用io_service::run() 函数来避免处理函同步。 如你所见,Asio库保证回调句柄仅能被当前正在调用 io_service::run(). 函数的线程调用。 因此,在单线程中调用io_service::run() 能确保回调句柄不被并发运行。

    单线程通常是使用Asio开发应用程序最好的方式。下面是Asio在程序中的局限性,尤其是服务器方面,包括:

    • 操作需要较长时间处理才能完成时弱响应。

    • 在大规模的多处理机系统中表现不佳。

    如果你发现自己陷入这些局限时,一个可供选择的方法是创建一个每个线程都调用io_service::run() 的线程池。 不过,因为这允许并发操作,当访问一个共享、非线程安全的资源时,我们需要一个同步方式。

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/thread.hpp>
    #include <boost/bind.hpp>
    #include <boost/date_time/posix_time/posix_time.hpp>

    让我们从定义一个名为printer的类开始,这与前一个示例中的类很相似。这个类是上一个例子的扩展,这里我们使用两个并行的定时器。

    class printer
    {
    public:

    除了初始化一对boost::asio::deadline_timer 成员变量外,构造函数还初始化一个boost::asio::strand类型strand_ 成员变量。

    boost::asio::strand 对象保证:对于通过它来分派执行的众操作中,只有一个操作执行完成之后才允许进入下一个操作。 这种保证与多少个线程调用io_service::run() 无关。当然,如果不是通过一个boost::asio::strand对象分派, 或者通过其它不同的boost::asio::strand对象分派,这些操作仍旧可能是并发的。

      printer(boost::asio::io_service& io)
        : strand_(io),
          timer1_(io, boost::posix_time::seconds(1)),
          timer2_(io, boost::posix_time::seconds(1)),
          count_(0)
      {

    当开始同步操作时,每一个回调句柄都使用boost::asio::strand对象进行“包装”。strand::wrap() 函数返回一个新的通过boost::asio::strand对象自动分派的内部句柄。 通过同一boost::asio::strand对象对句柄进行“ 包装”,我们可以保证操作不会并发执行。

        timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1,this)));
        timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2,this)));
      }
    
      ~printer()
      {
        std::cout <<"Final count is "<< count_ <<"\n";
      }

    在一个多线程程序中,当访问同一共享资源时,异步操作必须是同步的。在本例中,print1 print2)函数使用的共享资源std::cout count_数据成员。

      void print1()
      {
        if(count_ <10)
        {
          std::cout <<"Timer 1: "<< count_ <<"\n";
          ++count_;
    
          timer1_.expires_at(timer1_.expires_at()+ boost::posix_time::seconds(1));
          timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1,this)));
        }
      }
    
      void print2()
      {
        if(count_ <10)
        {
          std::cout <<"Timer 2: "<< count_ <<"\n";
          ++count_;
    
          timer2_.expires_at(timer2_.expires_at()+ boost::posix_time::seconds(1));
          timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2,this)));
        }
      }
    
    private:
      boost::asio::strand strand_;
      boost::asio::deadline_timer timer1_;
      boost::asio::deadline_timer timer2_;
      int count_;
    };

    main函数中, io_service::run() 现在被两个线程调用:主线程和一个附加线程。这一切依赖于boost::thread对象来完成。

    正如它被一个单线程调用一样,io_service::run()的并发调用会一直持续到无任何“工作”可做。后台线程直到所有异步操作都完成后才会退出。

    int main()
    {
      boost::asio::io_service io;
      printer p(io);
      boost::thread t(boost::bind(&boost::asio::io_service::run,&io));
      io.run();
      t.join();
    
      return0;
    }







    Daytime.1 - 同步TCP daytime客户端

    本示例程序显示如何使用Asio来实现一个TCP客户端程序。

    让我们从添加必需的头文件开始。

    #include <iostream>
    #include <boost/array.hpp>
    #include <boost/asio.hpp>

    这个应用程序的目的是访问一个daytime服务器,因此我们需要用户去指定服务器。(如time-nw.nist.gov,IP亦可)。

    using boost::asio::ip::tcp;
    
    int main(int argc, char* argv[])
    {
      try
      {
        if(argc !=2)
        {
          std::cerr <<"Usage: client <host>"<< std::endl;
          return1;
        }

    所有使用asio的程序都至少需要一个io_service 对象。

        boost::asio::io_service io_service;

    我们需要把服务器的名称转化为TCP的节点,而该名称是通过应用程序的参数指定的。我们使用ip::tcp::resolver 对象来完成。

        tcp::resolver resolver(io_service);

    一个resolver对象获得一个query对象,并将其转换为节点列表.我们通过argv[1]中的服务器名称和服务名,在这里是 "daytime",构造一个query

        tcp::resolver::query query(argv[1],"daytime");   //相当与用名字换取ip的操作

    节点列表用ip::tcp::resolver::iterator类型的迭代器返回。 返回的iterator将采用ip::tcp::resolver::iterator 的默认构造函数来构造。

        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        tcp::resolver::iterator end;

    现在我们建立一个socket并连接之,由于获得的节点既有IPv4也有IPv6的。所以,我们需要依次尝试访问它们直到找到一个可以正常工作的。这样做可使得我们的程序独立于特定的IP版本。

        tcp::socket socket(io_service);
        boost::system::error_code error = boost::asio::error::host_not_found;
        while(error && endpoint_iterator != end)
        {
          socket.close();
          socket.connect(*endpoint_iterator++, error);
        }
        if(error)
          throw boost::system::system_error(error);

    连接打开后,现在我们需要做的就是读取daytime服务器的响应。

    我们使用boost::array来存放接收到的数据。 The boost::asio::buffer() 函数会自动确定array的长度来防止缓冲区溢出。我们也可以使用 char [] std::vector来代替boost::array

        for(;;)
        {
          boost::array<char,128> buf;
          boost::system::error_code error;
    
          size_t len = socket.read_some(boost::asio::buffer(buf), error);

    当服务器关闭连接时,ip::tcp::socket::read_some() 函数会以boost::asio::error::eof错误标志返回, 通过该错误标志,我们知道应该退出循环了。

          if(error == boost::asio::error::eof)
            break;// Connection closed cleanly by peer.
          elseif(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;
      }







    Daytime.2 - 同步TCP daytime服务器

    本示例示范如何使用Asio来实现一个TCP服务器程序。

    #include <ctime>
    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::tcp;

    我们先定义一个make_daytime_string()来产生需要发送给客户端的字符串.这个函数会在我们所有的daytime服务器上被使用。

    std::string make_daytime_string()
    {
      usingnamespace std;// For time_t, time and ctime;
      time_t now = time(0);
      return ctime(&now);
    }
    
    int main()
    {
      try
      {
        boost::asio::io_service io_service;

    新建一个ip::tcp::acceptor 对象来监听新的连接。该对象应遵守IPv4协议,监听TCP端口13

        tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(),13));

    这是一个iterative server,也就是说同一时间只能处理一个连接。建立一个表示与客户端的连接的socket, 然后等待客户端的连接。

        for(;;)
        {
          tcp::socket socket(io_service);
          acceptor.accept(socket);

    当客户端访问服务器时,获取当前时间,并传送给客户端。

          std::string message = make_daytime_string();
    
          boost::system::error_code ignored_error;
          boost::asio::write(socket, boost::asio::buffer(message),
              boost::asio::transfer_all(), ignored_error);
        }
      }

    最后,处理异常。

      catch(std::exception& e)
      {
        std::cerr << e.what()<< std::endl;
      }
    
      return0;
    }



  • 相关阅读:
    Eclipse配置Maven的本地仓库和阿里云镜像 加速Maven更新
    layui弹出层基础参数
    鼠标悬停设置layui tips提示框
    Java实现文本中的关键字高亮,匹配所有长度
    获取指定格式的系统时间
    截取过长的字符,多余的字符将由省略号代替
    身份证处理方法(15转18,出生日期隐藏)
    layDate面板出现红色花纹图案
    layer单选框 radio的问题总结
    Android--多线程之Handler
  • 原文地址:https://www.cnblogs.com/ghost240/p/2529194.html
Copyright © 2011-2022 走看看