Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1238649
  • 博文数量: 389
  • 博客积分: 2874
  • 博客等级: 少校
  • 技术积分: 3577
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 10:34
文章分类

全部博文(389)

文章存档

2020年(2)

2018年(39)

2017年(27)

2016年(3)

2015年(55)

2014年(92)

2013年(54)

2012年(53)

2011年(64)

分类: C/C++

2011-09-04 11:31:48

Asio 是一个跨平台的C++开发包用来处理网络和低级I/O编程,通过先进的C++方法为开发人员提供连续异步模型。
示例代码:
  void handle_read(const asio::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
      asio::async_write(socket_,
          asio::buffer(data_, bytes_transferred),
          make_custom_alloc_handler(allocator_,
            boost::bind(&session::handle_write,
              shared_from_this(),
              asio::placeholders::error)));
    }
  }

  void handle_write(const asio::error_code& error)
  {
    if (!error)
    {
      socket_.async_read_some(asio::buffer(data_),
          make_custom_alloc_handler(allocator_,
            boost::bind(&session::handle_read,
              shared_from_this(),
              asio::placeholders::error,
              asio::placeholders::bytes_transferred)));
    }
  }

/**************************************************************************/

对于一个网络程序的服务器端我们需要提供的是服务器的address,和服务开放的端口号port。
在asio库中首先我们必须使用一个io_service类来支持所有的IO功能。需要注意到是我们必须调用io_service_my.run()函数来开启IO服务的事件循环以使功能都能被正常使用。
boost::asio::io_service io_service_my;
现在我们可以基于这个io_service_my来关联构建一下几个类:
1. boost::asio::ip::tcp::acceptor acceptor_my(io_service_my);
因为LPD的实现是基于TCP传输协议,所以也使用了TCP的acceptor来接收client发来的连接。
2.  boost::asio::ip::tcp::resolver resolver_my(io_service_my);
boost::asio::ip::tcp::resolver::query query_my(address,port);
boost::asio::ip::tcp::endpoint endpoint_my = *resolver.resolve(query_my);
这几个类主要是用来实现对地址的解析和绑定终端节点到相应的开放端口号上。首先构造一个关联到io_service_my的解析器resolver_my。然后让解析器resolver_my执行resolve
()函数来解析query_my指定的address和port到一个终端节点endpoint_my上。我们会看到这个endpoint_my终端节点会被绑定到这个acceptor_my接收器上。
3. boost::asio::ip::tcp::socket socket_my(io_service_my);
定义一个基于TCP协议的socket关联到io_service_my对象上。
在这些准备工作做完后我们开始一些实际的动作:
/*
* 打开一个使用由endpoint_my指定的协议类型的接收器,这个protocol()函数会自动返回与endpoint_my关联的协

* 议类型。
*/

acceptor_my.open(endpoint_my.protocol());

/*
* 设置选项允许socket绑定到一个已经正在被使用的地址。
*/

acceptor_my.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true);

/*
* 把接收器绑定到已经被设置过的endpoint_my。
*/

acceptor_my.bind(endpoint_my);

/*
* 接收器开始侦听。
*/

acceptor_my.listen();

/*
* 以同步/异步方式开始接收连接。
*/

acceptor_my.accept(socket_my) //同步
acceptor_my.async_accept(socket_my,
  boost::bind(&handle_accept,boost::asio::placeholders::error));//异步
其中异步的侦听函数原型是:
template<
    typename SocketService,
    typename AcceptHandler>
void async_accept(
    basic_socket< protocol_type, SocketService > & peer,
    AcceptHandler handler);
handler所对应的函数在新连接接收完成后会被调用。这种异步方式实现回调的方法也类似于使用boost::asio::io_service::post(boost::bind(&handle_accept));
注意到bind函数中的&handle_accept,这是函数handle_accept的入口地址,也就是在接收完成后会调用的函数在这里我们可以继续进行下一步的处理,从socket_my中读取或者写
入数据。

/*
* 调用异步读函数,把接收的数据拷贝到buffer缓存中,其中buffer是由参数buffer_my构造,

* 而buffer_my本身可以是一个容器例如boost::array buffer_my,表示申请了
* 一个8K个char字符型空间。也可以使用例外一种方法实现buffer的构造例如
* boost::asio::buffer(data,size_t);其中data表示指向某种数据类型的指针,
* size_t则表示包含多少个该数据类型的元素。
*/

void handle_accept(boost::system::error_code error)
{
if(!error)
{
  boost::array buffer_my;
  boost::asio::async_read(socket_my, boost::asio::buffer(buffer_my),
   boost::bind(&handle_read, boost::asio::placeholders::error));
}
}
类似的写程序如下
boost::asio::async_write(socket_my, boost::asio::buffer(buffer_my),
   boost::bind(&handle_write,
     boost::asio::placeholders::error,
     boost::asio::placeholders::bytes_transferred));

最后在所有连接完成之后或是服务器停止的时候别忘记关掉连接。例如
socket_my.close();
acceptor_my.close();
至此一个基于boost::asio库的网络程序的框架就出来了,至于具体的设计类实现可以视需求而定。

/************************************************************/

同步方式

摘要:本文通过形像而活泼的语言简单地介绍了Boost::asio库的使用,作为asio的一个入门介绍是非常合适的,可以给人一种新鲜的感觉,同时也能让体验到asio的主要内容。本文来自网络,原文在这里。


目录 [隐藏]
ASIO的同步方式
自我介绍
示例代码
小结
ASIO的异步方式
自我介绍
示例代码
小结
ASIO的“便民措施”
端点
超时
统一读写接口
基于流的操作
用ASIO编写UDP通信程序
用ASIO读写串行口
演示代码
Boost.Asio是一个跨平台的网络及底层IO的C++编程库,它使用现代C++手法实现了统一的异步调用模型。


ASIO的同步方式
ASIO库能够使用TCP、UDP、ICMP、串口来发送/接收数据,下面先介绍TCP协议的读写操作。对于读写方式,ASIO支持同步和异步两种方式,首先登场的是同步方式,下面请同步方式自我介绍一下。


自我介绍
大家好!我是同步方式!


我的主要特点就是执着!所有的操作都要完成或出错才会返回,不过偶的执着被大家称之为阻塞,实在是郁闷~~(场下一片嘘声),其实这样 也是有好处的,比如逻辑清晰,编程比较容易。


在服务器端,我会做个socket交给acceptor对象,让它一直等客户端连进来,连上以后再通过这个socket与客户端通信, 而所有的通信都是以阻塞方式进行的,读完或写完才会返回。


在客户端也一样,这时我会拿着socket去连接服务器,当然也是连上或出错了才返回,最后也是以阻塞的方式和服务器通信。


有 人认为同步方式没有异步方式高效,其实这是片面的理解。在单线程的情况下可能确实如此,我不能利用耗时的网络操作这段时间做别的事 情,不是好的统筹方法。不过这个问题可以通过多线程来避免,比如在服务器端让其中一个线程负责等待客户端连接,连接进来后把socket交给另外的线程去 和客户端通信,这样与一个客户端通信的同时也能接受其它客户端的连接,主线程也完全被解放了出来。


我的介绍就有这里,谢谢大家!


示例代码
好,感谢同步方式的自我介绍,现在放出同步方式的演示代码(起立鼓掌!)。


服务器端


#include
#include


int main(int argc, char* argv[])
{
        using namespace boost::asio;
        // 所有asio类都需要io_service对象
        io_service iosev;
        ip::tcp::acceptor acceptor(iosev,
        ip::tcp::endpoint(ip::tcp::v4(), 1000));
        for(;;)
        {
                // socket对象
                ip::tcp::socket socket(iosev);
                // 等待直到客户端连接进来
                acceptor.accept(socket);
                // 显示连接进来的客户端
                std::cout << socket.remote_endpoint().address() << std::endl;
                // 向客户端发送hello world!
                boost::system::error_code ec;
                socket.write_some(buffer("hello world!"), ec);


                // 如果出错,打印出错信息
                if(ec)
                {
                        std::cout <<
                                boost::system::system_error(ec).what() << std::endl;
                        break;
                }
                // 与当前客户交互完成后循环继续等待下一客户连接
        }
        return 0;
}
客户端


#include
#include


int main(int argc, char* argv[])
{
        using namespace boost::asio;


        // 所有asio类都需要io_service对象
        io_service iosev;
        // socket对象
        ip::tcp::socket socket(iosev);
        // 连接端点,这里使用了本机连接,可以修改IP地址测试远程连接
        ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 1000);
        // 连接服务器
        boost::system::error_code ec;
        socket.connect(ep,ec);
        // 如果出错,打印出错信息
        if(ec)
        {
                std::cout << boost::system::system_error(ec).what() << std::endl;
                return -1;
        }
        // 接收数据
        char buf[100];
        size_t len=socket.read_some(buffer(buf), ec);
        std::cout.write(buf, len);


        return 0;
}
小结
从演示代码可以得知


ASIO的TCP协议通过boost::asio::ip名 空间下的tcp类进行通信。
IP地址(address,address_v4,address_v6)、 端口号和协议版本组成一个端点(tcp:: endpoint)。用于在服务器端生成tcp::acceptor对 象,并在指定端口上等待连接;或者在客户端连接到指定地址的服务器上。
socket是 服务器与客户端通信的桥梁,连接成功后所有的读写都是通过socket对 象实现的,当socket析 构后,连接自动断 开。
ASIO读写所用的缓冲区用buffer函 数生成,这个函数生成的是一个ASIO内部使用的缓冲区类,它能把数组、指针(同时指定大 小)、std::vector、std::string、boost::array包装成缓冲区类。
ASIO 中的函数、类方法都接受一个boost::system::error_code类 型的数据,用于提供出错码。它可以转换成bool测试是否出错,并通过boost::system::system_error类 获得详细的出错信息。另外,也可以不向ASIO的函数或方法提供 boost::system::error_code,这时如果出错的话就会直 接抛出异常,异常类型就是boost::system:: system_error(它是从std::runtime_error继承的)。
ASIO的异步方式
嗯?异步方式好像有点坐不住了,那就请异步方式上场,大家欢迎...


/***********************************************************************/
异步方式

大家好,我是异步方式


和同步方式不同,我从来不花时间去等那些龟速的IO操作,我只是向系统说一声要做什么,然后就可以做其它事去了。如果系统完成了操作, 系统就会通过我之前给它的回调对象来通知我。


在ASIO库中,异步方式的函数或方法名称前面都有“async_” 前缀,函数参数里会要求放一个回调函数(或仿函数)。异步操作执行 后不管有没有完成都会立即返回,这时可以做一些其它事,直到回调函数(或仿函数)被调用,说明异步操作已经完成。


在ASIO中很多回调函数都只接受一个boost::system::error_code参数,在实际使用时肯定是不够的,所以一般 使用仿函数携带一堆相关数据作为回调,或者使用boost::bind来绑定一堆数据。


另外要注意的是,只有io_service类的run()方法运行之后回调对象才会被调用,否则即使系统已经完成了异步操作也不会有任 务动作。


示例代码
好了,就介绍到这里,下面是我带来的异步方式TCP Helloworld 服务器端:


#include
#include
#include
#include
#include


using namespace boost::asio;
using boost::system::error_code;
using ip::tcp;


struct CHelloWorld_Service
{
        CHelloWorld_Service(io_service &iosev)
                :m_iosev(iosev),m_acceptor(iosev, tcp::endpoint(tcp::v4(), 1000))
        {}


        void start()
        {
                // 开始等待连接(非阻塞)
                boost::shared_ptr psocket(new tcp::socket(m_iosev));
                // 触发的事件只有error_code参数,所以用boost::bind把socket绑定进去
                m_acceptor.async_accept(*psocket,
                        boost::bind(&CHelloWorld_Service::accept_handler, this, psocket, _1));
        }


        // 有客户端连接时accept_handler触发
        void accept_handler(boost::shared_ptr psocket, error_code ec)
        {
                if(ec) return;
                // 继续等待连接
                start();
                // 显示远程IP
                std::cout << psocket->remote_endpoint().address() << std::endl;
                // 发送信息(非阻塞)
                boost::shared_ptr pstr(new std::string("hello async world!"));
                psocket->async_write_some(buffer(*pstr),
                        boost::bind(&CHelloWorld_Service::write_handler, this, pstr, _1, _2));
        }


        // 异步写操作完成后write_handler触发
        void write_handler(boost::shared_ptr pstr, error_code ec,
                size_t bytes_transferred)
        {
                if(ec)
                std::cout<< "发送失败!" << std::endl;
                else
                std::cout<< *pstr << " 已发送" << std::endl;
        }


        private:
                io_service &m_iosev;
                ip::tcp::acceptor m_acceptor;
};


int main(int argc, char* argv[])
{
        io_service iosev;
        CHelloWorld_Service sev(iosev);
        // 开始等待连接
        sev.start();
        iosev.run();


        return 0;
}
小结
在 这个例子中,首先调用sev.start()开 始接受客户端连接。由于async_accept调 用后立即返回,start()方 法 也就马上完成了。sev.start()在 瞬间返回后iosev.run()开 始执行,iosev.run()方法是一个循环,负责分发异步回调事件,只 有所有异步操作全部完成才会返回。


这 里有个问题,就是要保证start()方法中m_acceptor.async_accept操 作所用的tcp::socket对 象 在整个异步操作期间保持有效(不 然系统底层异步操作了一半突然发现tcp::socket没了,不是拿人家开涮嘛-_-!!!),而且客户端连接进来后这个tcp::socket对象还 有用呢。这里的解决办法是使用一个带计数的智能指针boost::shared_ptr,并把这个指针作为参数绑定到回调函数上。


一旦有客户连接,我们在start()里给的回调函数accept_handler就会被 调用,首先调用start()继续异步等待其 它客户端的连接,然后使用绑定进来的tcp::socket对象与当前客户端通信。


发送数据也使用了异步方式(async_write_some), 同样要保证在整个异步发送期间缓冲区的有效性,所以也用boost::bind绑定了boost::shared_ptr。

对于客户端也一样,在connect和read_some方法前加一个async_前缀,然后加入回调即可,大家自己练习写一写。


ASIO的“便民措施”
asio中提供一些便利功能,如此可以实现许多方便的操作。


端点
回到前面的客户端代码,客户端的连接很简单,主要代码就是两行:


...
// 连接
socket.connect(endpoint,ec);
...
// 通信
socket.read_some(buffer(buf), ec);
不过连接之前我们必须得到连接端点endpoint,也就是服务器地址、端口号以及所用的协议版本。


前面的客户端代码假设了服务器使用IPv4协议,服务器IP地址为127.0.0.1,端口号为1000。实际使用的情况是,我们经常只能知道服务器网络ID,提供的服务类型,这时我们就得使用ASIO提供的tcp::resolver类来取得服务器的端点了。





比如我们要取得163网站的首页,首先就要得到“”服务器的HTTP端点:


io_service iosev;
ip::tcp::resolver res(iosev);
ip::tcp::resolver::query query("","80"); // 80端口
ip::tcp::resolver::iterator itr_endpoint = res.resolve(query);
这里的itr_endpoint是一个endpoint的迭代器,服务器的同一端口上可能不止一个端点,比如同时有IPv4和IPv6 两种。现在,遍历这些端点,找到可用的:


// 接上面代码
ip::tcp::resolver::iterator itr_end; //无参数构造生成end迭代器
ip::tcp::socket socket(iosev);
boost::system::error_code ec = error::host_not_found;
for(;ec && itr_endpoint!=itr_end;++itr_endpoint)
{
        socket.close();
        socket.connect(*itr_endpoint, ec);
}
如果连接上,错误码ec被清空,我们就可以与服务器通信了:


if(ec)
{
        std::cout << boost::system::system_error(ec).what() << std::endl;
        return -1;
}
// HTTP协议,取根路径HTTP源码
socket.write_some(buffer("GET http:// HTTP/1.0 "));
for(;;)
{
        char buf[128];
        boost::system::error_code error;
        size_t len = socket.read_some(buffer(buf), error);
        // 循环取数据,直到取完为止
        if(error == error::eof)
        break;
        else if(error)
        {
                std::cout << boost::system::system_error(error).what() << std::endl;
                return -1;
        }


        std::cout.write(buf, len);
}
当所有HTTP源码下载了以后,服务器会主动断开连接,这时客户端的错误码得到boost::asio::error::eof,我们 要根据它来判定是否跳出循环。


ip::tcp::resolver::query 的构造函数接受服务器名和服务名。前面的服务名我们直接使用了端口号"80",有时 我们也可以使用别名,用记事本打开%windir%\system32\drivers\etc\services文件(Windows环境),可以看到 一堆别名及对应的端口,如:


echo           7/tcp                 # Echo
ftp           21/tcp                 # File Transfer Protocol (Control)
telnet        23/tcp                 # Virtual Terminal Protocol
smtp          25/tcp                 # Simple Mail Transfer Protocol
time          37/tcp  timeserver     # Time
比如要连接163网站的telnet端口(如果有的话),可以这样写:


ip::tcp::resolver::query query("","telnet");
ip::tcp::resolver::iterator itr_endpoint = res.resolve(query);
超时
在网络应用里,常常要考虑超时的问题,不然连接后半天没反应谁也受不了。


ASIO库提供了deadline_timer类来支持定时触发,它的用法是:


// 定义定时回调
void print(const boost::system::error_code& /*e*/)
{
        std::cout << "Hello, world! ";
}

deadline_timer timer;
// 设置5秒后触发回调
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait(print);
这段代码执行后5秒钟时打印Hello World!


我们可以利用这种定时机制和异步连接方式来实现超时取消:


deadline_timer timer;
// 异步连接
socket.async_connect(my_endpoint, connect_handler/*连接回调*/);
// 设置超时
timer.expires_from_now(boost::posix_time::seconds(5));
timer.async_wait(timer_handler);
...
// 超时发生时关闭socket
void timer_handler()
{
        socket.close();
}
最后不要忘了io_service的run()方法。


统一读写接口
除了前面例子所用的tcp::socket读写方法(read_some, write_some等)以外,ASIO也提供了几个读写函数,主要有这么几个:


read、write、read_until、write_until
当然还有异步版本的


async_read、async_write、async_read_until、async_write_until
这些函数可以以统一的方式读写TCP、串口、HANDLE等类型的数据流。


我们前面的HTTP客户端代码可以这样改写:


...
//socket.write_some(buffer("GET http:// HTTP/1.0 "));
write(socket,buffer("GET http:// HTTP/1.0 "));
...
//size_t len = socket.read_some(buffer(buf), error);
size_t len = read(socket, buffer(buf), transfer_all() ,error);
if(len) std::cout.write(buf, len);
这个read和write有多个重载,同样,有错误码参数的不会抛出异常而无错误码参数的若出错则抛出异常。


本例中read函数里的transfer_all()是一个称为CompletionCondition的对象,表示读取/写入直接缓 冲区装满或出错为止。另一个可选的是transfer_at_least(size_t),表示至少要读取/写入多少个字符。


read_until和write_until用于读取直到某个条件满足为止,它接受的参数不再是buffer,而是boost::asio:: streambuf。


比如我们可以把我们的HTTP客户端代码改成这样:


boost::asio::streambuf strmbuf;
size_t len = read_until(socket,strmbuf," ",error);
std::istream is(&strmbuf);
is.unsetf(std::ios_base::skipws);
// 显示is流里的内容
std::copy(std::istream_iterator(is),
    std::istream_iterator(),
    std::ostream_iterator(std::cout));
基于流的操作
对于TCP协议来说,ASIO还提供了一个tcp::iostream。用它可以更简单地实现我们的HTTP客户端:


ip::tcp::iostream stream("", "80");
if(stream)
{
        // 发送数据
        stream << "GET http:// HTTP/1.0 ";
        // 不要忽略空白字符
        stream.unsetf(std::ios_base::skipws);
        // 显示stream流里的内容
        std::copy(std::istream_iterator(stream),
        std::istream_iterator(),
        std::ostream_iterator(std::cout));
}
用ASIO编写UDP通信程序
ASIO的TCP协议通过boost::asio::ip名空间下的tcp类进行通信,举一返三:ASIO的UDP协议通过boost::asio::ip名空间下的udp类进行通信。


我 们知道UDP是基于数据报模式的,所以事先不需要建立连接。就象寄信一样,要寄给谁只要写上地址往门口的邮箱一丢,其它的事各级邮局 包办;要收信用只要看看自家信箱里有没有信件就行(或问门口传达室老大爷)。在ASIO里,就是udp::socket的send_to和 receive_from方法(异步版本是async_send_to和asnync_receive_from)。


下面的示例代码是从ASIO官方文档里拿来的(实在想不出更好的例子了:-P):


服务器端代码


//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff
// (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying
// file LICENSE_1_0.txt or
// copy at )
//


#include
#include
#include
#include
#include


using boost::asio::ip::udp;


std::string make_daytime_string()
{
        using namespace std; // For time_t, time and ctime;
        time_t now = time(0);
        return ctime(&now);
}


int main()
{
        try
        {
                boost::asio::io_service io_service;
                // 在本机13端口建立一个socket
                udp::socket socket(io_service, udp::endpoint(udp::v4(), 13));


                for (;;)
                {
                        boost::array recv_buf;
                        udp::endpoint remote_endpoint;
                        boost::system::error_code error;
                        // 接收一个字符,这样就得到了远程端点(remote_endpoint)
                        socket.receive_from(boost::asio::buffer(recv_buf),
                        remote_endpoint, 0, error);


                        if (error && error != boost::asio::error::message_size)
                                throw boost::system::system_error(error);


                        std::string message = make_daytime_string();
                        // 向远程端点发送字符串message(当前时间)    
                        boost::system::error_code ignored_error;
                        socket.send_to(boost::asio::buffer(message),
                        remote_endpoint, 0, ignored_error);
                }
        }
        catch (std::exception& e)
        {
                std::cerr << e.what() << std::endl;
        }


        return 0;
}
客户端代码

//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff
// (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or
//  copy at )
//


#include
#include
#include


using boost::asio::ip::udp;


int main(int argc, char* argv[])
{
        try
        {
                if (argc != 2)
                {
                        std::cerr << "Usage: client " << std::endl;
                        return 1;
                }


                boost::asio::io_service io_service;
                // 取得命令行参数对应的服务器端点
                udp::resolver resolver(io_service);
                udp::resolver::query query(udp::v4(), argv[1], "daytime");
                udp::endpoint receiver_endpoint = *resolver.resolve(query);


                udp::socket socket(io_service);
                socket.open(udp::v4());
                // 发送一个字节给服务器,让服务器知道我们的地址
                boost::array send_buf  = { 0 };
                socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);
                // 接收服务器发来的数据
                boost::array recv_buf;
                udp::endpoint sender_endpoint;
                size_t len = socket.receive_from(
                boost::asio::buffer(recv_buf), sender_endpoint);


                std::cout.write(recv_buf.data(), len);
        }
        catch (std::exception& e)
        {
                std::cerr << e.what() << std::endl;
        }


        return 0;
}
用ASIO读写串行口
ASIO不仅支持网络通信,还能支持串口通信。要让两个设备使用串口通信,关键是要设置好正确的参数,这些参数是:波特率、奇偶校验 位、停止位、字符大小和流量控制。两个串口设备只有设置了相同的参数才能互相交谈。


ASIO提供了boost::asio::serial_port类,它有一个set_option(const SettableSerialPortOption& option)方法就是用于设置上面列举的这些参数的,其中的option可以是:


serial_port::baud_rate 波特率,构造参数为unsigned int
serial_port::parity 奇偶校验,构造参数为serial_port::parity::type,enum类型,可以是none, odd, even。
serial_port::flow_control 流量控制,构造参数为serial_port::flow_control::type,enum类型,可以是none software hardware
serial_port::stop_bits 停止位,构造参数为serial_port::stop_bits::type,enum类型,可以是one onepointfive two
serial_port::character_size 字符大小,构造参数为unsigned int
演示代码
#include
#include
#include


using namespace std;
using namespace boost::asio;


int main(int argc, char* argv[])
{
        io_service iosev;
        // 串口COM1, Linux下为“/dev/ttyS0”
        serial_port sp(iosev, "COM1");
        // 设置参数
        sp.set_option(serial_port::baud_rate(19200));
        sp.set_option(serial_port::flow_control(serial_port::flow_control::none));
        sp.set_option(serial_port::parity(serial_port::parity::none));
        sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));
        sp.set_option(serial_port::character_size(8));
        // 向串口写数据
        write(sp, buffer("Hello world", 12));


        // 向串口读数据
        char buf[100];
        read(sp, buffer(buf));


        iosev.run();
        return 0;
}
上面这段代码有个问题,read(sp, buffer(buf))非得读满100个字符才会返回,串口通信有时我们确实能知道对方发过来的字符长度,有时候是不能的。


如果知道对方发过来的数据里有分隔符的话(比如空格作为分隔),可以使用read_until来读,比如:


boost::asio::streambuf buf;
// 一直读到遇到空格为止
read_until(sp, buf, ' ');
copy(istream_iterator(istream(&buf)>>noskipws),
        istream_iterator(),
        ostream_iterator(cout));
另外一个方法是使用前面说过的异步读写+超时的方式,代码如下:


#include
#include
#include


using namespace std;
using namespace boost::asio;
void handle_read(char *buf,boost::system::error_code ec,
std::size_t bytes_transferred)
{
        cout.write(buf, bytes_transferred);
}


int main(int argc, char* argv[])
{
        io_service iosev;
        serial_port sp(iosev, "COM1");
        sp.set_option(serial_port::baud_rate(19200));
        sp.set_option(serial_port::flow_control());
        sp.set_option(serial_port::parity());
        sp.set_option(serial_port::stop_bits());
        sp.set_option(serial_port::character_size(8));


        write(sp, buffer("Hello world", 12));


        // 异步读
        char buf[100];
        async_read(sp, buffer(buf), boost::bind(handle_read, buf, _1, _2));
        // 100ms后超时
        deadline_timer timer(iosev);
        timer.expires_from_now(boost::posix_time::millisec(100));
        // 超时后调用sp的cancel()方法放弃读取更多字符
        timer.async_wait(boost::bind(&serial_port::cancel, boost::ref(sp)));


        iosev.run();
        return 0;
}




阅读(1673) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~