局域网内双升游戏的设计(二)--系统框架

来源:互联网 发布:廖雪峰javascript下载 编辑:程序博客网 时间:2024/04/28 14:07

服务器客户端关系

        一般来说,服务器与客户端在交流过程中,都会有自身的状态,也有很多逻辑的判断。这些可以放在服务器,也可以放在客户端。如果把功能分别放在两端的话,会造成逻辑比较混乱且不同步的现象。比如,在抢亮的过程中,可能有先后顺序,某个客户端亮主后,如果先在客户端上显示,但是消息到达服务器后,发现已经有人先亮了,就需要重新显示。这样一来,会造成逻辑的复杂化。在本程序中,采用的是服务器保存所有的状态,处理所有的逻辑。比如,客户端在点击亮主后,做的事情就是发一个消息给服务器,不做任何显示操作,等待服务器传来亮主的消息后再显示。出牌后,也是直接发消息给服务器,并等待服务器的计算结果后再显示出去的牌。客户端只会按照服务器命令做动作的。这样一直瘦客户端的形式,能很大程度上降低整个程序的复杂度。但是会增加消息数和服务器的负担。

        ps:现在暂未实现掉线重连机制。

消息定义

        在程序中,所有的消息采用字符串的形式封装,每个消息定义中有一个消息类型的字符串,还有这个消息包含的多个参数。主要定义如下:

typedef std::map<std::string, std::string> ParamMap; class SESSIONBASE_API CMsg{public:    std::string& operator []( const std::string& key )    {        return m_param[key];    }    std::string& operator []( const char* key )    {        return m_param[key];    }private:    std::string m_type;    ParamMap m_param;};


        这样,可以很方便地设置或者获取某个消息的某个参数:

CMsg discardMsg;discardMsg.getType() = CMsgDef::MSGTYPE_TODISCARD;discardMsg[CMsgDef::COMMON_PARAM_INDEX] = ToString( pState->m_zhuangIndex );

        将消息转换为一个流格式,于是,在CSessionBasesendMsg中,通过调用m_pWrapper->wrapMsg( pMsg, m_sendBuffer, len );来将CMsg对象转换为一个可以发送的字符串序列。本程序中采用的是XML格式封装,即:m_pWrapper是一个CXmlMsgWrapper对象。如果希望改变封装格式或者对消息进行适当加密的话,可以新建一个CMsgWrapper的子类,并且实现wrapMsgunWrapMsg两个函数即可。

服务器状态机

图 1.             服务器中类图

         在服务器中,主要的类如上图所示,CShengjiGamerMgr是总的调度,所有用户的连接都是通过它来接受的,每当有新的一桌游戏开始时,就新建一个CShengjiGamer对象。每个CShengjiGamer对象中最多保存4Session,分别代表4个不同的玩家。CShengjiGamer中还有一个CGamerStateBase,这是个状态机,使用了状态模式,不同的状态的逻辑处理不一样,要响应的消息也不一样。所以,每个状态机重载的时候要实现各自不同的消息处理函数。这样可以避免在不同状态下,收到客户端的错误消息(或者因为网络延时造成的超时消息)而导致的状态混乱。

        状态切换时,要新建一个新的状态机,并且删除旧的状态机,类似代码如下:

    if ( SHENGJI_PLAYER_COUNT == readyCount )    {        CGamerStateBase* pNewState = new CCompetePointState( m_players, m_initingPlayers, m_pState );        delete this;        return pNewState;    }

        在各个状态中,需要保存当前游戏的数据,就放在m_pState中。在每个状态间来回传递。

          参考代码+编译后的程序:http://download.csdn.net/detail/hustxyj/7024091
0 0
原创粉丝点击