最后更新于 .

接着上一篇文章: 有限状态机的C++实现(1)-epoll状态机,我们今天来介绍更复杂和深入的部分。 为什么会在标题中提到bayonet这个开源项目呢?笔者本人一直想要写一套架构优美、功能完善的异步server框架,也看过很多朋友、同事实现的版本,虽然功能上基本能满足需求,但是架构上我却始终觉得是有瑕疵的,直到后来和同事讨论,发现可以让一个客户端请求的到来作为一个session,而之后的每一次与其他server的交互都可以看作是一次状态转化,才感觉架构比较合理了。 简单来说即,一个session从开始到介绍会经历两种状态机的变化:

  • 1.业务逻辑层面的状态变化,例如先验证登录态,再验证权限,再获取用户资料
  • 2.每一个与其他server交互的socket自身的状态变化,如recv、send、等,而socket的状态变化会触发逻辑层的状态变化。

按照这种思路,目前的代码开发已经完成了70%,即可以正常的进行一个session的开始和结束,主要还缺一些细节的代码,比如超时的检测及超时之后的处理,健全的统计之类。好了,我们来用vs看一下代码的整体类图(图压缩比较严重,请单击后查看):

1

每个类的用处已经在途中简单说明了,这里就不再赘述,我们重点来看一下用这个框架来实现一个逻辑server时需要做哪些事情。 svr2目录下的main.cpp即实现了一个最简单的server,我们按部分来看其实现:

1.逻辑层状态的定义

class CAppFsmLogic1 : public CAppFsmBase
{
public:
    virtual ~CAppFsmLogic1 () {}
    virtual int HandleEntry(CActionInfoSet *pActionInfoSet, CAppActorBase* pAppActor)
    {
        static CActionFirst actionFirst;
        StActionInfoParam param;
        param.id = 1;
        param.ip = "127.0.0.1";
        param.port = 100;
        param.protoType = PROTO_TYPE_UDP;
        param.pAction = &actionFirst;
        param.actionType = ACTIONTYPE_SENDONLY;
        param.timeout_ms = 1000;
 
        CActionInfo * pActionInfo = new CActionInfo();
        pActionInfo->Init(param);
        pActionInfo->SetAppActor(pAppActor);
        pActionInfoSet->Add(pActionInfo);
        return 0;
    }
    virtual int HandleProcess(CActionInfoSet *pActionInfoSet, CAppActorBase* pAppActor)
    {
        trace_log("HandleProcess");
        set<CActionInfo*> &setAction = pActionInfoSet->GetActionSet();
        for(set<CActionInfo*>::iterator it = setAction.begin(); it != setAction.end(); ++it)
        {
            trace_log("error no:%d",(*it)->GetErrno());
        }
        return APP_FSM_RSP;//代表要回复客户端啦
    }
    virtual int HandleExit(CActionInfoSet *pActionInfoSet, CAppActorBase* pAppActor)
    {
        return 0;
    }
};

CAppFsmLogic1是一个逻辑层的状态:

  • 1.HandleEntry代表当这个状态第一次进入的时候要做的事情,其函数中创建了一个向其他server发包的action(CActionFirst的定义我们在后面介绍)。
  • 2.HandleProcess代表当这个状态的所有action都完成时需要做的事情,return APP_FSM_RSP;代表向客户端回包

看到这里大家应该很奇怪,对于每一个socket,判断收包长度以及受到包之后的解包在哪里完成呢?所以我们还需要定义action:

2.action的定义

#define APP_FSM_LOGIC1 2000
 
class CAppFsmLogic1;
class CActionFirst : public IAction
{
public:
    // 为发送打包
    int HandleEncodeSendBuf(
            IActor* pSocketActor,
            IActor* pAppActor,
            string & strSendBuf,
            int &len)
    {
        trace_log("send");
        strSendBuf="woainizhende111111";
        len = strSendBuf.size();
        return 0;
    }
 
    // 回应包完整性检查
    int HandleInput(
            IActor* pSocketActor,
            IActor* pAppActor,
            const char *buf,
            int len)
    {
        return len;
    }
 
    // 回应包解析
    int HandleDecodeRecvBuf(
            IActor* pSocketActor,
            IActor* pAppActor,
            const char *buf, 
            int len)
    {
        CAppActorBase * app_actor = new CAppActorBase();
        app_actor->AttachFrame(pSocketActor->GetFrame());
        app_actor->AttachCommu(pSocketActor);
        app_actor->ChangeState(APP_FSM_LOGIC1);
        trace_log("listen tcp HandleDecodeRecvBuf");
        return 0;
    }
};

每个函数的意义已经在代码中说明了,可以看出在HandleDecodeRecvBuf中创建逻辑层的actor: app_actor,并ChangeState为APP_FSM_LOGIC1。 最后就是main函数的实现了:

3.main函数实现

int main(int argc, const char *argv[])
{
    CBayonetFrame srv;
    StFrameParam param;
    param.ip="0.0.0.0";
    param.port = 10001;
    param.bKeepcnt= true;
    //param.protoType = PROTO_TYPE_UDP;
    param.protoType = PROTO_TYPE_TCP;
    param.pAction = new CActionFirst();
 
    srv.Init(param);
    srv.RegFsm(APP_FSM_LOGIC1,new CAppFsmLogic1());
    srv.Process();
    return 0;
}

注释的部分是可以随时切换TCP还是UDP的。 当然作为一个server来说,这里还是太过简单了,比如信号的处理等都没有加上,但是笔者认为那是业务代码需要做的逻辑,所以并没有放到框架中。 OK,整个项目的结构就是这个样子了。 但是也不得不说点扫兴的话,由于笔者最近有另外一个项目需要投入大量的精力,所以该项目的更新可能会被延缓,这是我所不愿意看到的,所以很希望有志同道合的朋友能够加入到这个项目的开发中来,一起把这个事情做出来。 按照我当初的想法,压力测试框架fuload已经就绪了,等到bayonet完成,我们就用fuload来测试一下bayonet的性能究竟如何。

最后,附上bayonet的项目地址: http://code.google.com/p/bayonet/ 十分欢迎大家感兴趣的朋友与我联系。

Pingbacks

Pingbacks已打开。

Trackbacks

引用地址

评论

  1. linoom

    linoom on #

    楼主,bayonet链接不能下载呀

    Reply

    1. Dante

      Dante on #

      没有提供download,直接用svn 更新源码即可。

      Reply

  2. xavier

    xavier on #

    源码很少注释。
    对于新人来说看起来有点困难呢~~
    不懂的地方能直接请教你么~

    Reply

    1. Dante

      Dante on #

      呃,这个确实是我没时间写太多注释,可以直接问我的。

      Reply

  3. ..

    .. on #

    学术研究?

    Reply

    1. 朱念洋

      朱念洋 on #

      很早的代码了,现在想想其实有很多当时没有考虑到的地方。

      Reply

发表评论