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