使用Boost.Asio编写通信程序

使用Boost.Asio编写通信程序

转自http://cpp.ezbty.org/content/science_doc/%E4%BD%BF%E7%94%A8boostasio%E7%BC%96%E5%86%99%E9%80%9A%E4%BF%A1%E7%A8%8B%E5%BA%8F

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

Boost.Asio是一个跨平台的网络及底层IO的C++编程库,它使用现代C++手法实现了统一的异步调用模型。

ASIO的同步方式

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

自我介绍

大家好!我是同步方式!

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

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

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

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

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

示例代码

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

服务器端

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

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 <iostream>
#include <boost/asio.hpp>

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 <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>

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 <tcp :: socket > psocket ( new tcp :: socket (m_iosev ) ) ;
                 // 触发的事件只有error_code参数,所以用boost::bind把socket绑定进去
                m_acceptor. async_accept ( *psocket,
                        boost :: bind ( &CHelloWorld_Service :: accept_handlerthis, psocket, _1 ) ) ;
         }

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

         // 异步写操作完成后write_handler触发
         void write_handler (boost :: shared_ptr <std :: string > 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网站的首页,首先就要得到“www.163.com”服务器的HTTP端点:

io_service iosev ;
ip :: tcp :: resolver res (iosev ) ;
ip :: tcp :: resolver :: query query ( "www.163.com", "80" ) ;  //www.163.com 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 <a href="http : //www.163.com" title="http://www.163.com">http://www.163.com</a> 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 ( "www.163.com", "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也提供了几个读写函数,主要有这么几个:

readwrite、read_until、write_until
当然还有异步版本的
async_read、async_write、async_read_until、async_write_until
这些函数可以以统一的方式读写TCP、串口、HANDLE等类型的数据流。

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

...
//socket.write_some(buffer("GET <a href="http://www.163.com" title="http://www.163.com">http://www.163.com</a> HTTP/1.0 "));
write (socket,buffer ( "GET <a href="http : //www.163.com" title="http://www.163.com">http://www.163.com</a> 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 < char > (is ),
    std :: istream_iterator < char > ( ),
    std :: ostream_iterator < char > (std :: cout ) ) ;

基于流的操作

对于TCP协议来说,ASIO还提供了一个tcp::iostream。用它可以更简单地实现我们的HTTP客户端:

ip :: tcp :: iostream stream ( "www.163.com""80" ) ;
if (stream )
{
         // 发送数据
        stream  <<  "GET <a href="http : //www.163.com" title="http://www.163.com">http://www.163.com</a> HTTP/1.0 ";
         // 不要忽略空白字符
        stream. unsetf (std :: ios_base :: skipws ) ;
         // 显示stream流里的内容
        std :: copy (std :: istream_iterator < char > (stream ),
        std :: istream_iterator < char > ( ),
        std :: ostream_iterator < char > (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 <a href="http://www.boost.org/LICENSE_1_0.txt" title="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)
//

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

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 < char, 1 > 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 <a href="http://www.boost.org/LICENSE_1_0.txt" title="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>)
//

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

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

int main ( int argc,  char * argv [ ] )
{
         try
         {
                 if  (argc  ! = 2 )
                 {
                        std :: cerr  <<  "Usage: client <host>"  << 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 < char, 1 > send_buf   =  { 0  } ;
                socket. send_to (boost :: asio :: buffer (send_buf ), receiver_endpoint ) ;
                 // 接收服务器发来的数据
                boost :: array < char, 128 > 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 <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

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 < char > (istream ( &buf ) >>noskipws ),
        istream_iterator < char > ( ),
        ostream_iterator < char > ( cout ) ) ;

另外一个方法是使用前面说过的异步读写+超时的方式,代码如下:

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Boost.Asio是一个开源的跨平台网络编程库,它可以用于构建高效的异步I/O应用程序。以下是一些关于Boost.Asio的学习资源: 1. Boost.Asio官方文档:Boost.Asio的官方文档提供了详细的介绍和使用指南,包括教程、参考文档和示例代码。文档链接:https://www.boost.org/doc/libs/1_77_0/doc/html/boost_asio.html。 2. Boost.Asio学习指南:这是一篇由Boost.Asio的核心开发者编写的学习指南,它介绍了Boost.Asio的基本概念和使用方法,并提供了一些示例代码。指南链接:https://think-async.com/Asio/asio-1.18.1/doc/asio/overview/core.html。 3. Boost.Asio教程:这是一系列由Boost.Asio社区成员编写的教程,涵盖了从基础到高级的内容。教程链接:https://github.com/chriskohlhoff/asio/tree/master/asio/doc/tutorial。 4. Boost.Asio实战:这是一本关于使用Boost.Asio构建网络应用的书籍,它详细介绍了Boost.Asio使用方法和实践经验,并提供了大量示例代码。书籍链接:https://www.oreilly.com/library/view/boostasio-c/9781785283079/。 希望这些资源能够帮助您学习和掌握Boost.Asio。 ### 回答2: boost.asio是一个基于C++的网络编程库,为开发人员提供了一套强大而灵活的工具来实现异步网络通信。 该库的教程涵盖了boost.asio的核心概念、基本用法和高级功能。首先,教程介绍了异步编程的概念,以及为什么应该使用异步编程来处理网络通信。然后,它讲解了boost.asio的核心组件,例如IoServiceSocketBuffer,以及如何使用它们进行网络连接和数据传输。 教程还会指导开发人员如何使用boost.asio处理不同类型的网络协议,例如TCP/IP和UDP。开发人员将学习如何创建服务器和客户端,并使用boost.asio的异步操作来处理连接和数据传输。教程还会介绍一些常见的网络编程任务,例如并发服务器和多线程编程,以及如何使用boost.asio来解决这些问题。 除了基本内容外,教程还涵盖了boost.asio的高级功能,例如SSL加密、定时器和信号处理。开发人员将学习如何使用SSL加密来保护网络通信,以及如何使用定时器来处理超时和延迟。此外,教程还会介绍如何使用信号处理来处理中断和异常情况。 总的来说,boost.asio教程为开发人员提供了一个全面的参考资料,帮助他们掌握这个强大的网络编程库。无论是初学者还是有经验的开发人员,都可以从中学习到如何使用boost.asio来构建高效可靠的网络应用程序。 ### 回答3: boost.asio是一个用于异步I/O操作的C++库。它提供了一个面向对象的接口,可以用于处理网络编程、文件I/O和串口通信等操作。boost.asio的设计目标是高效和可扩展性。 boost.asio教程是一个系统介绍boost.asio库的学习材料。该教程通常包含以下内容: 1. 引言:介绍boost.asio库的作用和特性。解释为什么选择boost.asio而不是其他库。 2. 环境配置:指导读者如何安装和配置boost.asio库。包括下载和安装boost库、配置编译器和连接器等步骤。 3. 基本概念:介绍boost.asio库的基本概念和术语。例如,异步操作、回调函数、handler等。 4. 网络编程:教授如何使用boost.asio库进行网络编程。包括创建Socket对象、建立连接、发送和接收数据等操作。 5. 文件I/O:介绍如何使用boost.asio库进行文件读写操作。教授如何打开文件、读取和写入数据等。 6. 串口通信:介绍如何使用boost.asio库进行串口通信。教授如何打开串口、发送和接收数据等。 7. 高级主题:介绍一些高级主题,例如多线程编程、定时器、SSL加密等。这些主题可以帮助读者更深入地理解和使用boost.asio库。 通过学习boost.asio教程,读者可以学会使用boost.asio库进行异步I/O操作。他们可以编写高性能的网络应用程序、文件处理程序和串口通信程序。同时,他们还能够了解到一些高级主题,从而扩展他们的技能和知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值