ASIO 之 剖 析--(5) 以Proactor模式的角度来剖析ASIO

MORE INFO AT : WWW.LEEHAO.ORG

___________________________________________________________________________________________________________________________________


为了使用这些功能boost在其上面包装了一个io_service壳,从而屏蔽了关于完成端口所实现的一些细节性的描述,而只需要关注io_service即可并通过它将socket或者是串口等提供io服务的一些对象进行关联。这些对于端口的关联均在task_io_serivce和win_icop_io_service完成。在io_service.hpp文件中我们可以看到如下的语句:

 

 

#ifdefined(BOOST_ASIO_HAS_IOCP)

namespacedetail { typedef win_iocp_io_serviceio_service_impl;}

#else

namespacedetail { typedef task_io_service io_service_impl;}

#endif

….

private:

  typedefdetail::io_service_impl impl_type;

impl_type& impl_

至此,io_service将提供一个接口,可将socket handle,以及serial handle等与完成端口进行相关联。

 

 

 

 

 

 

 

op_queue

  其中op_queue为相关操作的队列,系统所产生或者获得相关操作均由此队列中取得相关操作句柄。而其数据结构的定义如下:


 

l  Endpoint 对象:

下面我们就以一个具体的例子来说明使用asio进行同步方式下的网络通信。在详细介绍之前,我们首先要阐述一个概念:服务接入点---tcp::endpoint。其位于boost::asio::ip::tcp的名字空间中。其主要的作用就是:使用一个指定的端口或者IP地址构建endpoint对象,并将其绑定到socket对象上。

tcp::endpoint (tcp::v4(), 13) 使用端口13及ipv4协议在本地主机上创建一个服务接入点[服务器模式]。tcp::endpoint(“ip.address”,port)客服端模式。

 

我们由endpoint的实现中可以看出其对于posixsocket编程中对于socket对象中参数设置的封装。

 

endpoint::endpoint()

  : data_()

{

  data_.v4.sin_family = AF_INET;

  data_.v4.sin_port = 0;

  data_.v4.sin_addr.s_addr =INADDR_ANY;

}

endpoint::endpoint(intfamily, unsigned short port_num)

  : data_()

{

  using namespace std;         // For memcpy.

  if (family == PF_INET)

  {

    data_.v4.sin_family = AF_INET;

    data_.v4.sin_port =

     boost::asio::detail::socket_ops::host_to_network_short(port_num);

    data_.v4.sin_addr.s_addr = INADDR_ANY;

  }

  else

  {

    data_.v6.sin6_family = AF_INET6;

    data_.v6.sin6_port =

     boost::asio::detail::socket_ops::host_to_network_short(port_num);

    data_.v6.sin6_flowinfo = 0;

    boost::asio::detail::in6_addr_type tmp_addr= IN6ADDR_ANY_INIT;

    data_.v6.sin6_addr = tmp_addr;

    data_.v6.sin6_scope_id = 0;

  }

}

上式中host_to_network_short其作用是完成主机字节序到网络字节序的转换。其上封装了posixsocket编程下的htons(value),这样封装的目的是以更加易于理解的方式进行程序的编写。

 

其中data_的数据类型为:

  union data_union

  {

    boost::asio::detail::socket_addr_typebase;

    boost::asio::detail::sockaddr_storage_typestorage;

    boost::asio::detail::sockaddr_in4_typev4;

    boost::asio::detail::sockaddr_in6_typev6;

  } data_;

而data_.v4的类型为:sockaddr_in类型[其位于detail/socket_types.hpptypedefsockaddr_insockaddr_in4_type;],由此我们可以看出经过层层的封装,我们使用endpoint时候,无需再过度的关注底层的细节设置,而使我们能够更加关注于系统的业务逻辑方面的设计。

 

 

socket

下面我们就对socket类进行一些初步的分析。在ip/tcp.hpp文件中我们可以看到如下语句:typedefbasic_stream_socket<tcp> socket; 可以看出socket其真面目为:basic_stream_socket<tcp>下面就让我们走进 basic_stream_socket<tcp>一探究竟。我们在basic_stream_socket.hpp文中我们可以得到basic_stream_socket类的定义。其定义如下:

template<typename Protocol,

    typename StreamSocketService =stream_socket_service<Protocol> >

classbasic_stream_socket

  : publicbasic_socket<Protocol, StreamSocketService>

其继承了socket基类 basic_socket,而对basic_stream_socket进行初始化之前,首先会完成其基的初始化工作。经过这层由socketà=basic_stream_socket->basic_socket的主线,现在我们将目光转向更底层的类:basic_socket,来看看它为我们提供了什么样的服

务?在asio目录下的basic_socket.hpp文件中,我们找到了该类得定义,其形式如下:

 

template<typename Protocol, typename SocketService>

classbasic_socket

  : publicbasic_io_object<SocketService>,

public socket_base

 

而basic_io_object<SocketService>和socket_base分别在asio目录下的basic_io_object.hpp文件和socket_base.hpp文件中给出他们的定义。他们之间的关系可以用如下来表示:

这些socket通过namesspace socket_ops下的函数具体完成socket地址的bind,listen,accept,complete_iocp_accept,connect等功能的实现。下面我们就到命名空间socket_ops中一探究竟。

 

socket_ops

  该命名空间位于detail/socket_ops.hpp及detail/impl/socket_ops.ipp文件中,其主要的作用就是完成关于socket通讯的系统api层得操作。例如:我们在上层应用中写了如下语句时:

io_service ios;

ip::tcp::endpoint endpoint(tcp::v4(), 80);

ip::tcp::acceptor acceptor(ios, endpoint);

在底层所作的工作便有使用socket_ops空间内的bind函数将一个已设置地址与端口的地址结构struct sockaddr* addr与一个socket句柄进行绑定。而这些涉及socket与OS底层发生关系的操作均可以在socket_ops命名空间中寻得。关于其空间中所涉及的具体函数在这里不在赘述,可具体参考其源码。

 

boost::bind

  在我们使用asio库的时候,经常看见boost::bind函数,那么该函数的主要作用是什么呢?我们用boost::bind函数又能做哪些工作呢?下面我们就来探究一下该函数。总体来说,其位于boost命名空间中,而它的主要的作用描述为:其是标准函数std::bind1ststd::bind2nd的泛化形式。其支持任意函数对象,函数,函数指针,成员函数指针。其可以将一个指定的值绑定到任意一个参数上,或者将输入参数重新绑定到参数中的其它任何位置。例如,对于如下的代码:

int f(int a, int b)
{
    return a + b;
}
 
int g(int a, int b, int c)
{
    return a + b + c;
}

bind(f, 1, 2)其作用如同f(1, 2)一样,bind(g, 1, 2, 3)() 则等价于g(1, 2, 3).

 

 

acceptor

在创建 tcp::acceptor时候其定义为 在tcp.hpp文件中。

typedefbasic_socket_acceptor<tcp> acceptor; 而 basic_socket_acceptor的定义如下:

template<typename Protocol,

typename SocketAcceptorService =socket_acceptor_service<Protocol> > class basic_socket_acceptor

而在对acceptor进行实例化时候对其 Protocol参数为tcp,而SocketAcceptorService为socket_acceptor_service<Protocol>,因此其为basic_socket_acceptor所提供的模板参数为socket_acceptor_service<tcp>。因此我们在调用 tcp::socket::acceptor 进程对象的初始化时候我们获得的acceptor对象其实是使用basic_acceptor_service<tcp,socket_acceptor_serivce<tcp> > 对对象进行初始化工作。其中basic_acceptor_service的定义为template <typename Protocol,typenameSocketAcceptorService = socket_acceptor_service<Protocol> >。

下面我们就对进行分析basic_acceptor_serivce <tcp,socket_acceptor_serivce<tcp> >。从socket_acceptor_service.hpp文件中对于其定义可以看出其具有如下形式的继承方式。

template <typename Protocol>

class socket_acceptor_service

#if defined(GENERATING_DOCUMENTATION)

  : public boost::asio::io_service::service

#else

  : publicboost::asio::detail::service_base<socket_acceptor_service<Protocol>>

#endif

 

 

 

 

 

 

 

l  综述

从上述的描述中我们可以看出boost::asio对于传统的posix socket编程进行了有效的封装。例如:posixsocket编程中所使用的socket, listen, connect 等函数。 基本的逻辑概念描述:所有的IO操作均由io_service来充当中间层的功能,其主要负责上层程序与OS底层所提供的IO服务沟通,其ASIO中的所有操作均通过io_service与操作系统所提的服务进行关联。ASIO提供了io操作的一些核心功能。其所在的namespace路径如下

boost::asio::xxx

其下包括了如下的异步IO对象。

(1)ip::tcp::socket       网络套接字类,提供tcp方式的网络套接字  

主要的目的:完成网络套接字对象的初始化工作。

(2)ip::tcp::acceptor     连接接受类,其完成posixsocket api中的accept函数的功能,接受客户端的连接请求。

(3)  ip::udp::socket 提供udp方式的网络套接字

(4)  deadline_time 提供定时操作。为了能够完成网络的超时控制,可以由其中的定时对象来完成异步方式的定时操作。而在POSIX下其timer所提供的时间控制精度不足,无法满足毫秒或者更为精细的时间控制。为了获得更高的定时精度.我们通常采用select等方式。

而在POSIX下其测试网络连通状态的通常做法是由一个timer在指定的时间内发送一个heartbeat报文,用以测试网络的状态,我们也可在所发的heartbeat报文上打上timestamp用以观察网络的延迟。该种方式普遍应用在多种协议中:例如移动的CMPP,联通的SGIP,以及银联的POS交易协议。

 

 

detail/push_options.hpp中对相关编译器参数的识别,及不同编译器中所使用参数的的配置。

detail/socket_types.hpp 中则重新定义/更名了Xnix/windows平台下socket编程所有的各种数据结构.例如:微软平台:_MSC_VER , 塞班系统:__SYMBIAN32__,HPUnix: __hpux ,SUN: __sun, 及对于select的支持情况。

 

 

POSIX下的做法:

为了满足高性能的socket io 操作。我们使用select/poll/epoll等函数来完成存在着大规模并发io的网络通信。

 

服务端代码:下面是一个传统的POSIX socket开发的例子,在完成socket对象的建立后,

  inth_socket= socket (PF_INET, SOCK_STREAM , 0);

  …

struct sockaddr_in  serveraddr;

serveraddr.sin_family = AF_INET;                  //设置好协议族类型

serveraddr.sin_addr.s_addr =inet_addr (szListenIP) ;   //设置监听地址

serveraddr.sin_port  = htons ((short)iListenPort);     //设置监听端口

bind(h_socket,(structsockaddr*)&serveraddr ,sizeof(serveraddr)); //地址信息的绑定

  …

 listen (h_socket,1024);                                  //开始监听。

  …

FD_ZERO (…)

 FD_SET (…)

Iret=select(1024,&readset,NULL,NULL ,NULL); //poll, epoll 等高效的复用i/o

if (iret) {

iRecvedSkt_hd = accept  (h_socket,(struct sockaddr*)&csin_addr,&len) ; //接收客户端请求

    …

  pthread_create (&accptedThread, NULL ,StartBuinessThread ,(void*)&iRecvedSkt_hd);//启动一个线程对其进行服务。

 }

 

static void* StartBuinessThread (void*strThreadParams)

{ …}

 

 

 

 

 

 

 

 

 

 

 

参考文件:

1 Douglas C. SchmidtProactor Pattern .

2:  Proactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handlersfor Asynchronous Events

3:  MSDN online: http://msdn.microsoft.com/en-us/library/aa365198.aspx

 

附录1

 

一个简单的Reactor示例,我们以IO操作中的读操作为例。Reactor具有以下行为方式。

- 注册读就绪事件和相应的事件处理器

- 事件分离器等待事件

- 事件到来,激活分离器,分离器调用事件对应的处理器。

- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

相应的sample code如下所示:

class handler
{
public:
    virtual void onRead() = 0;
    virtual void onWrite() = 0;
    virtual void onAccept() = 0;
};

class dispatch
{
public:
    void poll()
    {
        // add fd in the set.
        //
        // poll every fd
        int c = select( 0, &read_fd,&write_fd, 0, 0 );
        if( c > 0 )
        {
            for each fdin the read_fd_set
           {    if fd can read
                   _handler->onRead();
               if fd can accept
                   _handler->onAccept();
            }

           for each fd in the write_fd_set
            {
               if fd can write
                   _handler->onWrite();
            }
        }
    }

    void setHandler( handler *_h )
    {
        _handler = _h;
    }

private:
    handler *_handler;
};

/// application
class MyHandler : public handler
{
public:
    void onRead()
    {
    }

    void onWrite()
    {
    }

voidonAccept()
    { }

};

附录2

完成端口(IOC)是用来实现高性能服务的一种常用方法,主要是通过操作系统提供的异步调用功能实现IO操作,可以通过很少的线程数实现高性能的并发服务。一个完成端口读写的例子,如下:

#define _WIN32_WINNT    0x0500

#include <cstdlib>

#include <clocale>

#include <ctime>

#include <iostream>                  

#include <vector>

#include <algorithm>

#include <winsock2.h>

#include <mswsock.h>

 

using namespace std;

 

#pragmacomment(lib,"ws2_32.lib")

#pragmacomment(lib,"mswsock.lib")

 

const intMAX_BUFFER_SIZE=1024;

const intPRE_SEND_SIZE=1024;

const intQUIT_TIME_OUT=3000;

constintPRE_DOT_TIMER=QUIT_TIME_OUT/80;

 

typedefenum{IoTransFile,IoSend,IoRecv,IoQuit} IO_TYPE;

 

typedef struct

{

    SOCKEThSocket;

    SOCKADDR_IN ClientAddr;

}PRE_SOCKET_DATA,*PPRE_SOCKET_DATA;

 

typedef struct

{

    OVERLAPPED     oa;

    WSABUF         DataBuf;

    char         Buffer[MAX_BUFFER_SIZE];

    IO_TYPE         IoType;

}PRE_IO_DATA,*PPRE_IO_DATA;

 

typedefvector<PPRE_SOCKET_DATA>     SocketDataVector;

typedefvector<PPRE_IO_DATA>        IoDataVector;

 

SocketDataVector    gSockDataVec;

IoDataVector        gIoDataVec;

 

CRITICAL_SECTION    csProtection;

 

char* TimeNow(void)

{

    time_tt=time(NULL);

     tm*localtm=localtime(&t);

    static chartimemsg[512]={0};

    

    strftime(timemsg,512,"%Z: %B %d %X,%Y",localtm);

    return timemsg;

}

 

BOOLTransFile(PPRE_IO_DATApIoData,PPRE_SOCKET_DATApSocketData,DWORDdwNameLen)

{

    pIoData->Buffer[dwNameLen-1]='\0';

    

    HANDLEhFile=CreateFile(pIoData->Buffer,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);

    BOOLbRet=FALSE;

     if(hFile!=INVALID_HANDLE_VALUE)

     {

        cout<<"Transmit File "<<pIoData->Buffer<<"to client"<<endl;

        pIoData->IoType=IoTransFile;

        memset(&pIoData->oa,0,sizeof(OVERLAPPED));

        *reinterpret_cast<HANDLE*>(pIoData->Buffer)=hFile;

        TransmitFile(pSocketData->hSocket,hFile,GetFileSize(hFile,NULL),PRE_SEND_SIZE,reinterpret_cast<LPOVERLAPPED>(pIoData),NULL,TF_USE_SYSTEM_THREAD);

        bRet=WSAGetLastError()==WSA_IO_PENDING;

     }

     else

        cout<<"Transmit File"<<"Error:"<<GetLastError()<<endl;

      return bRet;

}

 

DWORDWINAPIThreadProc(LPVOIDIocpHandle)

{

    DWORDdwRecv=0;

    DWORDdwFlags=0;

    

    HANDLEhIocp=reinterpret_cast<HANDLE>(IocpHandle);

    DWORDdwTransCount=0;

    PPRE_IO_DATApPreIoData=NULL;

    PPRE_SOCKET_DATApPreHandleData=NULL;

 

    while(TRUE)

     {

        if(GetQueuedCompletionStatus(hIocp,&dwTransCount,

            reinterpret_cast<LPDWORD>(&pPreHandleData),

            reinterpret_cast<LPOVERLAPPED*>(&pPreIoData),INFINITE))

        {

            if(0==dwTransCount&&IoQuit!=pPreIoData->IoType)

            {

                cout<<"Client:"

                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)

                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)

                    <<" is closed"<<endl;

 

                closesocket(pPreHandleData->hSocket);

 

                EnterCriticalSection(&csProtection);

                    IoDataVector::iteratoritrIoDelete=find(gIoDataVec.begin(),gIoDataVec.end(),pPreIoData);

                    SocketDataVector::iteratoritrSockDelete=find(gSockDataVec.begin(),gSockDataVec.end(),pPreHandleData);

                  delete *itrIoDelete;

                  delete *itrSockDelete;

                  gIoDataVec.erase(itrIoDelete);

                  gSockDataVec.erase(itrSockDelete);

                 LeaveCriticalSection(&csProtection);

                continue;

            }

            

            switch(pPreIoData->IoType){

            case IoTransFile:

                cout<<"Client:"

                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)

                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)

                    <<" Transmit finished"<<endl;

                CloseHandle(*reinterpret_cast<HANDLE*>(pPreIoData->Buffer));

                goto LRERECV;

                

            case IoSend:

                cout<<"Client:"

                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)

                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)

                    <<" Send finished"<<endl;

 

LRERECV:

                pPreIoData->IoType=IoRecv;

                pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;

                memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));

                WSARecv(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,

                    &dwRecv,&dwFlags,

                    reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);

               break;

  case IoRecv:

                cout<<"Client:"

                    <<inet_ntoa(pPreHandleData->ClientAddr.sin_addr)

                    <<":"<<ntohs(pPreHandleData->ClientAddr.sin_port)

                    <<"recv finished"<<endl;

                pPreIoData->IoType=IoSend;

                

                if(!TransFile(pPreIoData,pPreHandleData,dwTransCount))

                {

                    memset(&pPreIoData->oa,0,sizeof(OVERLAPPED));

                    strcpy(pPreIoData->DataBuf.buf,"File transmit error!\r\n");

                    pPreIoData->DataBuf.len=strlen(pPreIoData->DataBuf.buf);

                    

                    WSASend(pPreHandleData->hSocket,&pPreIoData->DataBuf,1,

                        &dwRecv,dwFlags,

                        reinterpret_cast<LPWSAOVERLAPPED>(pPreIoData),NULL);

                }

                break;

          caseIoQuit:

                goto LQUIT;

          default:

                ;

            }

        }    

     }

   LQUIT:

    return 0;

}

 

HANDLEhIocp=NULL;

SOCKEThListen=NULL;

 

BOOLWINAPIShutdownHandler(DWORDdwCtrlType)

{

    PRE_SOCKET_DATAPreSockData={0};

    PRE_IO_DATAPreIoData={0};

    PreIoData.IoType=IoQuit;

    if(hIocp)

     {

        PostQueuedCompletionStatus(hIocp,1,

        reinterpret_cast<ULONG_PTR>(&PreSockData),

        reinterpret_cast<LPOVERLAPPED>(&PreIoData));

         cout<<"Shutdown at "<<TimeNow()<<endl<<"waitfor a moment please"<<endl;

        

        for(intt=0;t<80;t+=1)

        {

            Sleep(PRE_DOT_TIMER);

            cout<<".";

        }

        

        CloseHandle(hIocp);

     }

    

    inti=0;

      for(;i<gSockDataVec.size();i++)

     {

        PPRE_SOCKET_DATApSockData=gSockDataVec[i];

        closesocket(pSockData->hSocket);

        delete pSockData;

     }

      for(i=0;i<gIoDataVec.size();i++)

     {

        PPRE_IO_DATApIoData=gIoDataVec[i];

        delete pIoData;

     }

      DeleteCriticalSection(&csProtection);

    if(hListen)

        closesocket(hListen);

 

    WSACleanup();

    exit(0);

    return TRUE;

}

 

LONGWINAPIMyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)

{

    ShutdownHandler(0);

    return EXCEPTION_EXECUTE_HANDLER;

}

 

u_shortDefPort=8182;

 

intmain(intargc,char **argv)

{

    if(argc==2)

        DefPort=atoi(argv[1]);

 

    InitializeCriticalSection(&csProtection);

    SetUnhandledExceptionFilter(MyExceptionFilter);

    SetConsoleCtrlHandler(ShutdownHandler,TRUE);

 

    hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

 

    WSADATA data={0};

    WSAStartup(0x0202,&data);

 

    hListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if(INVALID_SOCKET==hListen)

     {

        ShutdownHandler(0);

     }

    

    SOCKADDR_IN addr={0};

    addr.sin_family=AF_INET;

    addr.sin_port=htons(DefPort);

    

    if(bind(hListen,reinterpret_cast<PSOCKADDR>(&addr),

        sizeof(addr))==SOCKET_ERROR)

     {

        ShutdownHandler(0);

     }

    

    if(listen(hListen,256)==SOCKET_ERROR)

        ShutdownHandler(0);

 

    SYSTEM_INFO si={0};

    GetSystemInfo(&si);

    si.dwNumberOfProcessors<<=1;

 

    for(inti=0;i<si.dwNumberOfProcessors;i++)

     {

        

        QueueUserWorkItem(ThreadProc,hIocp,WT_EXECUTELONGFUNCTION);

     }

    

    cout<<"Startup at "<<TimeNow()<<endl

        <<"work on port "<<DefPort<<endl

        <<"press CTRL+C toshutdown"<<endl<<endl<<endl;

 

    while(TRUE)

     {

        intnamelen=sizeof(addr);

        memset(&addr,0,sizeof(addr));

        SOCKEThAccept=accept(hListen,reinterpret_cast<PSOCKADDR>(&addr),&namelen);

         if(hAccept!=INVALID_SOCKET)

        {

            cout<<"accept aclient:"<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port)<<endl;

             PPRE_SOCKET_DATApPreHandleData=newPRE_SOCKET_DATA;

            pPreHandleData->hSocket=hAccept;

            memcpy(&pPreHandleData->ClientAddr,&addr,sizeof(addr));

            CreateIoCompletionPort(reinterpret_cast<HANDLE>(hAccept),hIocp,reinterpret_cast<DWORD>(pPreHandleData),0);

            PPRE_IO_DATApPreIoData=new(nothrow) PRE_IO_DATA; 

             if(pPreIoData)

            {

                EnterCriticalSection(&csProtection);

                gSockDataVec.push_back(pPreHandleData);

                gIoDataVec.push_back(pPreIoData);

                LeaveCriticalSection(&csProtection);

                memset(pPreIoData,0,sizeof(PRE_IO_DATA));

                pPreIoData->IoType=IoRecv;

                pPreIoData->DataBuf.len=MAX_BUFFER_SIZE;

                pPreIoData->DataBuf.buf=pPreIoData->Buffer;

                DWORDdwRecv=0;

                DWORDdwFlags=0;

                WSARecv(hAccept,&pPreIoData->DataBuf,1,&dwRecv,&dwFlags,reinterpret_cast<WSAOVERLAPPED*>(pPreIoData),NULL);

            }

            else

            {

                delete pPreHandleData;

                closesocket(hAccept);

            }

        }

     }

    return 0;

}

 

附录3

一个proactor模式的示例。

classAsyIOProcessor

{

public:

    void do_read()

    {

        //send read operation to OS

        // read io finished.and dispatchnotification

        _proactor->dispatch_read();

    }

 

private:

    Proactor *_proactor;

};

 

class Proactor

{

public:

    void dispatch_read()

    {

        _handlerMgr->onRead();

    }

 

private:

    HandlerManager *_handlerMgr;

};

 

classHandlerManager

{

public:

    typedef std::list<Handler*>HandlerList;

public:

    void onRead()

    {

        // notify all the handlers.

        std::for_each( _handlers.begin(),_handlers.end(), onRead );

    }

private:

    HandlerList *_handlers;

};

 

class Handler

{

public:

    virtual void onRead() = 0;

};

 

// applicationlevel handler.

class MyHandler :public Handler

{

public:

    void onRead()

    {

    }

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值