博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
boost asio(初学示例)
阅读量:6425 次
发布时间:2019-06-23

本文共 8968 字,大约阅读时间需要 29 分钟。

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

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

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

#include 
#include

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

#include 

使用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.1 中的程序,使用Asio的异步回调功能在定时器中演示一个异步等待。

#include 
#include
#include

使用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.2中的例子,使定时器每秒被激活一次。例子将示范如何给你的函数指针传递附加参数。

#include 
#include
#include
#include

使用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::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;}

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

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

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

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

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

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

#include 
#include
#include
#include
#include

让我们从定义一个名为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;}

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

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

#include 
#include
#include

这个应用程序的目的是访问一个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
"<< 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
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; }

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

#include 
#include
#include
#include
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;}

转载于:https://www.cnblogs.com/ghost240/archive/2012/05/31/2529194.html

你可能感兴趣的文章
开发自己的Web服务处理程序(以支持Ajax框架异步调用Web服务方法)
查看>>
ref和out
查看>>
黑客教父详解账号泄露全过程:1亿用户已泄露
查看>>
程序员必须软件
查看>>
Canvas里的globalCompositeOperation
查看>>
解决Unable to locate theme engine in module_path: "pixmap"
查看>>
贝叶斯文本分类c#版
查看>>
Centos安装KDE或GNOME
查看>>
Eclipse & IDEA 中常用的快捷键
查看>>
javascript ---IPhone滑动解锁
查看>>
table固定行和表头
查看>>
<每天读一点职场心理学>读书笔记
查看>>
Android权限大全代码
查看>>
android 判断SIM卡是哪个运营商
查看>>
删除N天前的M(天)个目录 、删除N天前最后修改的文件 ForFiles, dos command 批处理命令cmd/bat...
查看>>
十进制数1~n中1出现的次数
查看>>
PostgreSQL 的 语法分析的理解(五)
查看>>
[转载]Visual Studio 2010敏捷利剑:详解Scrum
查看>>
Java Collection: List、Set、 Map、 HashMap、 Hashtable、 Vector
查看>>
T-SQL查询进阶--流程控制语句
查看>>