wangchenxicool

博客园 首页 联系 管理

对于阻塞式socket编程,libevent是一个不错的选择.而关于如何利用libevent来写自己的程序,网上的这方面的资料较少。
仅有几个例子:libevent-echosrv1.c,libevent-echosrv2.c,libevent-echosrv-buffered.c,看本文之前可先download下来看
一看.

1. 对于阻塞式,处理逻辑可放在一个函数里,很清楚.

foo()
{
    recv(
100);   //1.读100个byte
    handle();    //2.处理
    send();      //3.将handle()产生的data全发走
    done();      //4. happy.
}

而用libevent把这个函数写成非阻塞式,写法如下(psuedo-code):

foo()
{
    event_set(EV_READ,myrecv);
    event_add();
}

myrecv()
{
    
if (EV_TIMEOUT);        //暂不考虑timeout
    n=recv();               //1
    if (n<0)
    {
        
if (errno!=EAGAIN && errno!=EINTR)
            err();          
//系统错
        else
            event_add();    
//还要读
    }
    
else if (n==0)
        err();             
//对方close fd;
    else
    {
        
if (已读到100)
            handle();      
//2
        else
            event_add();    
//还要读
    }
}

handle()
{
    real_handle();        
//真正的处理函数
    event_set(EV_WRITE,mysend);
    event_add();    
}

mysend()
{
    n
=send();                //3
    if (n<0)
    {
        
if (errno!=EAGAIN && errno!=EINTR)
            err();         
//系统错
        else
            event_add();    
//还要写
    }
    
else if (n==0)
        err();             
//对方close fd;
    else
    {
        
if (已发完)
            done();        
//4 终于到达终点,happy
        else
            event_add();    
//还要写
    }
}


可见在非阻塞式中逻辑是分布在各个函数里,形成一个可顺序执行的函数链.


2.考虑一个复杂点的情况

proxy()
{
    recvP1(
100);    //1.读100个byte
    handleP1();     //2.处理protocol-1
    sendP2();       //3.将protocol-1转化成protocol-2后,发走

    recvP2(
100);    //4.读100个byte
    handleP2();     //5.处理protocol-2
    sendP1();       //6.protocol-2转化成protocol-1后,发走

    done();        
//7. happy.
}


如果直接将逻辑分布在函数链里,将会造成protocol-1与protocol-2绞在一起,不利于软件分层.
这时可用到callback(使用callback常常就是为了解决分层).

foo()
{
    P1
=malloc();
    P1
->callback = sendP2;           // P1->callback表示收完P1包之后的动作.
    event_set(EV_READ,recvP1,P1);    // fd可读时调用recvP1;
    event_add();
}

recvP1(fd,,P1)
{    
    n
=recv();
    
if (读结束)         //为使用代码简洁,以下忽略n<0,n=0,TIMEOUT等情况
        handleP1();     //处理protocol-1
    else
        event_add();    
//还要读
}

sendP1(P1)
{
    n
=send();
    
if (已发完)
        done();        
// 结束,happy.
    else
        event_add();
}

handleP1(P1)
{
    P2 
=get_P2(P1);                         //真正的处理函数,p1包-->p2包
    event_set(EV_WRITE,p1->callback,P2);    //实际上是调用sendP2(),自此进入protocol-2这一层.
    event_add();
}

recvP2(P2)
{    
    P2 
= malloc();
    P2
->callback = sendP1;     // 设定收完p2包之后的动作
    n=recv();
    
if (读结束)
        handleP2();          
//处理protocol-1
    else
        event_add();         
//还要读
}

sendP2(P2)
{
    n
=send();
    
if (已发完) {
        event_set(EV_READ,recvP2,p2);
        event_add();
    }
    
else
        event_add();
}

handleP2(P2)
{
    P1 
=get_P1(P2);                         //真正的处理函数,p2包-->P1包
    event_set(EV_WRITE,p2->callback,P1);    //实际上是调用sendP1(),自此返回protocol-1这一层.
    event_add();
}


在实际应用时还要考虑以下几点
1.connect/recv/send这些函数失败后的处理,
2.请求块的malloc/free
3.TIMEOUT问题.
4.buffer管理

可见写非阻塞式程序要比阻塞式程序复杂得多。

对libevent有兴趣的,可加QQ群:64559783,一起交流.

posted on 2011-09-01 18:54  wangchenxicool  阅读(374)  评论(0)    收藏  举报