ASIO库学习笔记

C++语言: 临时自用代码
使用起来很简单,我们只需要掌握几个类模板。第一个是 asio :: demuxer ,这是一个核心事件驱动类模板(我自己的意译),它很重要,为所有的异步 I / O 对象提供多路事件触发。

asio :: demuxer d; 第二个类模板是 asio :: socket_acceptor ,它整合了从创建到监听一个 socket 的所有过程。 asio :: socket_acceptor acceptor; asio :: socket_acceptor 的构造有两种方式,具体参看原文档。第二种方式为 asio :: socket_acceptor( asio :: demuxer & d , const ENDPOINT & endpoint , int listen_backlog = 0) 类模板将与 asio :: demuxer 构建联系,其中 ENDPOINT asio :: ipv4 :: tcp :: endpoint asio :: ipv4 :: udp :: endpoint ENDPOINT 的构造方式很简单,有如下两种 endpoint ( unsigned short port_num) 这种构造常用于服务器, port_num 为端口号, IP 地址为 INADDR_ANY endpoint ( unsigned short port_num , const asio :: ipv4 :: address & addr) 这种构造常用于客户端, asio :: ipv4 :: address 的构造直接填入十进制的 IP 地址即可,如 asio :: ipv4 :: address h( "192.168.1.1");

创建 acceptor 对象后,就可以接受新的连接了,使用成员函数 async_accept( Socket & peer , Handler handler) Socket & peer 是一个与 asio :: demuxer 对象相联系的 asio :: stream_socket 对象 , Handler 指向一个函数的句柄,表 示在新的连接完成后进行的操作,这种类似的特性,在 windows 网络编程中,一般称作完成例程( completion routines )。于是,我们利用这个句柄来反复进行 async_accept accept 后就是 I / O 操作了,而类似地,异步读写操作有 asio :: async_read asio :: async_write 等等,他们通常也有一个 Handle 句柄可以传递,利用此来进行“完成例程”式的反复读写。好了好了,我发现这样写很累,这样吧,还是用 code 来表达最为生动。其实 asio 库的文档里附带了几个 examples ,看完他们也就差不多掌握了。我以其中一个例子 chat 的服务器端

代码为原型,将其简化,附加详细的注释,来演示如何用 asio 库来构建服务器。 chat_server . cpp

#include <algorithm>

#include <cstdlib>

#include <deque>

#include <iostream>

#include <list>

#include <set>

#include <boost/bind.hpp>

#include <boost/shared_ptr.hpp>

#include <boost/enable_shared_from_this.hpp>

#include "asio.hpp"

#include "chat_message.hpp"

//----------------------------------------------------------------------

typedef std :: deque < chat_message > chat_message_queue;

//---------------------------------------------------------------------- // 含有纯虚函数的一个基类,与主题无关,基本可以无视

class chat_participant

{

public : 

virtual ~ chat_participant()

{} 

virtual void deliver( const chat_message & msg) = 0;

};

typedef boost :: shared_ptr < chat_participant > chat_participant_ptr;

//---------------------------------------------------------------------- // 以下函数我将其清空了,可以无视它,作为黑盒处理。

class chat_room { };

//---------------------------------------------------------------------- // boost::enable_shared_from_this<chat_session>是一个smart pointers,它能通过shared_from_this()从一个成员函数内部获取当前对象。

class chat_session   : public chat_participant ,     public boost :: enable_shared_from_this < chat_session > { public : // 构造,这里要了解的操作主要是:new生成了一个asio::stream_socket对象,并将其与demuxer联系在一起,受其事件驱动。 

chat_session( asio :: demuxer & d , chat_room & r)     : socket_( d ),       room_( r)   {   } // 返回socket对象   asio::stream_socket& socket()   {     return socket_;   } //新的连接建立,开始读操作,这里的asio::async_read_n是一个异步读函数,指定了接受长度chat_message::header_length(enum定义了为4)的数据。在该异步读结束后,将会调用boost:bind()绑定的函数体 // 这里有个重点,就是boost:bind函数,具体可以参看boost文档。因为成员函数指针&chat_session:: handle_read_header并非一个函数体(function objects),这里就必须使用bind来将其转换为一个函数体。 

void start() 

{   

room_ . join( shared_from_this());   

asio :: async_read_n( socket_ , read_msg_ . data (), chat_message :: header_length , boost :: bind(           & chat_session :: handle_read_header , shared_from_this (), asio :: placeholders :: error ,           asio :: placeholders :: last_bytes_transferred)); 

}

void deliver( const chat_message & msg

{   

bool write_in_progress = ! write_msgs_ . empty();   

write_msgs_ . push_back( msg);   

if ( ! write_in_progress)   

{     

asio :: async_write_n( socket_ , write_msgs_ . front (). data (), write_msgs_ . front (). length (), boost :: bind( & chat_session :: handle_write , shared_from_this (), asio :: placeholders :: error ,             asio :: placeholders :: last_bytes_transferred));   

} 

} // start()中的异步读结束后,将执行该函数,同样,在该函数中,利用handle来传递另一个读成功后的函数操作。 // 这里传递到了chat_session::handle_read_body,其实与主题不大相关,这只是通常用以网络传输的一种数据组织方式。 // 在交互数据相当频繁和复杂的网络游戏中,都是采用这种方式,可变长数据包。它有固定的头大小,其中包含了数据包的长度和类型以及时间戳等等特性。 // 每次接收的时候先获取已知长度的头,解析出其中包含的信息,然后针对接收数据体。 // start()->handle_read_header->handle_read_body->handle_reade_header-〉……构成了一个循环,反复接收处理。

void handle_read_header( const asio :: error & error , size_t last_length

{   

if ( ! error && last_length > 0 && read_msg_ . decode_header()) 

{     

asio :: async_read_n( socket_ , read_msg_ . body (), read_msg_ . body_length (), boost :: bind( & chat_session :: handle_read_body , shared_from_this (), asio :: placeholders :: error ,             asio :: placeholders :: last_bytes_transferred));     }

else

{     

room_ . leave( shared_from_this());   

} 

}

void handle_read_body( const asio :: error & error , size_t last_length

{   

if ( ! error && last_length > 0)   

{       room_ . deliver( read_msg_);     

asio :: async_read_n( socket_ , read_msg_ . data (), chat_message :: header_length , boost :: bind( & chat_session :: handle_read_header , shared_from_this (), asio :: placeholders :: error ,             asio :: placeholders :: last_bytes_transferred));   

}   

else   

{     

room_ . leave( shared_from_this());   

} 

} // 异步写,没什么好说的,更异步读类似。 

void handle_write( const asio :: error & error , size_t last_length

{   

if ( ! error && last_length > 0)   

{     

write_msgs_ . pop_front();     

if ( ! write_msgs_ . empty())     

{       

asio :: async_write_n( socket_ , write_msgs_ . front (). data (), write_msgs_ . front (). length (), boost :: bind( & chat_session :: handle_write , shared_from_this (), asio :: placeholders :: error , asio :: placeholders :: last_bytes_transferred));     

}   

}   

else   

{     

room_ . leave( shared_from_this());   

} 

}

private :  
asio :: stream_socket socket_;  
chat_room & room_;  
chat_message read_msg_;  
chat_message_queue write_msgs_; };

typedef boost :: shared_ptr < chat_session > chat_session_ptr;

//----------------------------------------------------------------------

class chat_server
{
public : // 构造函数,一定看明白它做了什么工作。  
chat_server( asio :: demuxer & d , const asio :: ipv4 :: tcp :: endpoint & endpoint) : demuxer_( d ), acceptor_( d , endpoint)  
{
chat_session_ptr new_session( new chat_session( demuxer_ , room_));    
acceptor_ . async_accept( new_session -> socket (), boost :: bind( & chat_server :: handle_accept , this , new_session ,           asio :: placeholders :: error));  
} // 同样地,通过handle来传递一个重复接收动作。  
void handle_accept( chat_session_ptr session , const asio :: error & error)  
{    
if ( ! error)    
{      
session -> start();      
chat_session_ptr new_session( new chat_session( demuxer_ , room_));      
acceptor_ . async_accept( new_session -> socket (), boost :: bind( & chat_server :: handle_accept , this , new_session ,             asio :: placeholders :: error));    
}    
else if ( error == asio :: error :: connection_aborted)    
{      
acceptor_ . async_accept( session -> socket (), boost :: bind( & chat_server :: handle_accept , this , session ,             asio :: placeholders :: error));    
}  
}
private : // 注意这里的asio::demuxer& demuxer_,它只是一个指针地址,指向main中的demuxer,也就是说,核心事件驱动只有一个!所有的asio相关操作,都有它来控制。 
asio :: demuxer & demuxer_;  
asio :: socket_acceptor acceptor_;  
chat_room room_; };

typedef boost :: shared_ptr < chat_server > chat_server_ptr; typedef std :: list < chat_server_ptr > chat_server_list;

//----------------------------------------------------------------------

int main( int argc , char * argv [])
{  
try  
{    
if ( argc < 2)    
{      
std :: cerr << "Usage: chat_server <port> [<port> ...] /n ";       return 1;    
}

    asio :: demuxer d;
    chat_server_list servers;    
    for ( int i = 1; i < argc; ++ i)    
    {
    // 根据多个端口生成多个服务器,      
    using namespace std; // For atoi.      
    asio :: ipv4 :: tcp :: endpoint endpoint( atoi( argv [ i ]));      
    chat_server_ptr server( new chat_server( d , endpoint));      
    servers . push_back( server);    
    }
    d . run();  
}  
catch ( asio :: error & e)  
{    
std :: cerr << e << " /n ";  
}  
catch ( std :: exception & e
{    
std :: cerr << "Exception: " << e . what() << " /n ";  
}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值