多进程手游流程分析

来源:互联网 发布:μcos ii源码v2.86 编辑:程序博客网 时间:2024/05/01 11:03
1. gate启动时,向master注册
2. 动态增加新的gate的流程:
   开启新的gate时,向master注册,master收到newgate的注册请求后,向所有的gameserver发送PtcM2G_NewGateConnected消息
   gameserver会把此gate的IP port 名字等信息保存下来 放到gatelink的列表里面去,重连管理器reconnmanager会尝试对此
   newgate进行连接,此时,所有的gameserver就会和这个newgate建立连接了
3. 动态增加新的gameserver流程:
   开启新的game时,向master注册:RpcG2M_RegisterGs2Ms. master会处理此消息.
   master处理此消息时, 会把所有的gate信息返回给此newgame, newgame收到回应
   后,会尝试和这些所有的gate建立连接.
   newgame向master注册的时机:void MSLink::OnConnectSucceed(UINT32 dwConnID), 即和master连接成功后,会开始注册.
   那么newgame在何处处理向master注册的回应呢? 在gameserver的文件:rpcg2m_registergs2ms.cpp的函数
   void RpcG2M_RegisterGs2Ms::OnReply(const RegisterGs2MsArg &roArg, const RegisterGs2MsRes &roRes, const CUserData &roUserData)
   所以不要在game中看到类似于rpcg2m_xxx的协议而感到惊讶
4. master向login注册:
   login处理master注册,在dblink.h中完成【在loginserver中,此处是文件名命名错误,dblink.h应该叫mslink.h才对】
   在loginserver的dblink.cpp文件中,函数KKSG::ErrorCode MsLink::RegisterMs(UINT32 dwConnID, UINT32 dwZoneID, UINT32 dwServerID)
   会处理master的注册,login会保存master的连接号 区号 服务器编号 可以参见MsLink::RegisterMs的代码 一目了然
5. 玩家登陆流程【登录服是所有服务器公用的,只有一个登录服】
   客户端初始化时,从资源服务器拉取服务器列表,每个服务器信息包括:大区【电信或网通】 服务器编号 服务器名字
   【此处了解下CDN的概念】
   此处假设选则其中的 电信--游戏10服  这个服务器
   选中后,点确定,来确认选择游戏10服这个服务器,此时有个选择gate和链接gate的事件发生,且有认证账号密码的事件发生
   输入登录界面的账号名和密码,点击开始游戏按钮,会拉取到该账号名对应的角色列表,client会和返回的QueryGateRes中的
   gate的IP和端口建立链接. client和gate建立链接后,会与login断开链接.


6. client在querygateip过程中,验证账号名和密码的流程:
   A. 如果登录方式为LOGIN_PASSWORD
 则说明不适用第三方SDK,使用账户名+密码的方式登录,此时,建立一个数据库查询任务DBCheckPasswordTask
      参数为账户名和密码,查询成功之后,会确认此账号名+密码是否正确,如果正确,则返回,此时会调用RpcC2I_QueryGateIP::OnDelayReplyRpc【rpcc2i_querygateip.cpp】
      客户端就会拿到gate的信息,从而与gate建立连接【获取gate的方法为随机获取,不是根据ping值或者当前gate的承载人数】
   B. 如果登陆方式为LOGIN_SNDA_PF,则需要做token验证【不知道此方式为何物】
   C. 如果登陆方式为QQ登陆或者微信登陆LOGIN_QQ_PF || LGOIN_WECHAT_PF
      也需要做TOKEN认证


7. 账号+密码认证和TOKEN认证的区别
   A. 账号+密码认证,是在querygateip过程中,到数据库中查询,是否存在此账号+密码.
   B. TOKEN认证,是走的HTTP协议,用到了rapidjson的方法. 有固定的HTTP协议参数格式,并且有私钥,私钥的值参见文件TXLoginTask.cpp的开始处:
const char* TXLoginTask::m_pf = "qq";
    const char* TXLoginTask::m_PlatFormSecrtKey = "authleajoy!#$^abcd1234";
http参数构建好之后,会向webthread添加一个任务,在webthread中的run函数,通过curl_multi_perform来执行这些任务.



8. login服务器中如何获取gate列表 存在于GateIP.txt中,此文件位于exe目录下
   DownloadGateIPTableTask
   打开gateip.txt可以看到服务器的配置.
   
9. 客户端界面上显示的服务器列表有三个服: 张三一服 李四一服 王五一服
10. 客户端是如何确定服务器ID的?
    querygateip协议中,假定登录方式为账号+密码,账号+密码匹配后,向DB线程发送一个DBGetLastServerTask任务,查询该账户上一次
登录的服务器ID
select serverid from lastlogin where userid= account
查询的结果即是上一次登录的服务器ID,此是确定服务器ID的方法一
如果此账号为新号,数据库中不会有此账号上次登录服务器的记录,那怎么确定该选择哪个服务器呢?
此时会调用 GetRecommandServerID 获取一个推荐的服务器ID
获取推荐服务器的算法:如果某个服务器拥有推荐服务器的标示【新服 火爆 推荐 人满等等标示】,则可以将此服务器设置为推荐服务器
如果账号不是新号 也没有找到上次登录的服务器 则也采用个和新号相同的推荐算法
11. 通过步骤10,客户端拿到了推荐的服务器ID,还有服务器的如下信息:
roRes.set_gateip(pAddr->ip);
roRes.set_port(pAddr->port);
roRes.set_serverid(pUserInfo->serverID);
roRes.set_zonename(pInfo->zoneName);
roRes.set_servername(pInfo->serverName);
此信息为gateserver的信息而非gameserver的信息,因为对于客户端来说,它只是跟gate连接,gameserver则是透明的,客户端并不知道其存在

12. querygateip协议返回后,客户端连接此gate,通过IP和端口,
    gateserver收到客户端新的连接后,会向此连接推送一个消息:
clientlink.cpp: CClientLink::OnPassiveConnect-->sessionmanager::onNewConnection(uint32 dwConnID) 
在onNewConnection中,gateserver向客户端发送消息:PtcT2C_LoginChallenge challengePtc; 且将此连接的session的状态改为SESSION_CHALLENGE
客户端在收到gateserver的 PtcT2C_LoginChallenge 消息后,会向gateserver发送登录请求:RpcC2T_ClientLoginRequest 请求里面带有服务器ID,
openid,token,以及平台[ios, pc, android], 和版本号
gateserver如何处理client的登录请求呢: 修改此session的gameserverID, 修改session的state为SESSION_DBVERIFY
然后gateserver向master发送RpcT2M_LoginRequestNew 协议,等待master的处理,master返回后,会在RpcT2M_LoginRequestNew::OnReply 中处理登录结果
如果master对gate向其发出的RPCT2M_LOGINRequestNew返回的结果为成功, 即roRes.result()=ERR_SUCCESS,则修改此client的session->state=SESSION_LOGIN,
然后解除此异步过程中对该session绑定的定时器 rpct2m_loginrequestnew.cpp:56行  pSessionManager->StopLoginTimer(m_sessionID);
然后调用 DelayRepy(pSession, roRes.result(), const_cast<LoginRequestNewRes&>(roRes).mutable_accountdata()); 向 void RpcC2T_ClientLoginRequest::OnDelayReplyRpc回应 
在 void RpcC2T_ClientLoginRequest::OnDelayReplyRpc 中,会对客户端的回包做字段填充,填充个方法如下:
if(nErrCode == KKSG::ERR_SUCCESS)
{
KKSG::LoadAccountData* data = (KKSG::LoadAccountData*)roUserData.m_pUserPtr;
*roRes.mutable_accountdata() = *data;
}
也就是把账户信息返回给客户端-->可以了解下KKSG::LoadAccountData的结构,如下:
<PGData>
    <Name>LoadAccountData</Name>
    <Fields>
      <PGField>
        <typeName>string</typeName>
        <variableName>account</variableName>
        <specify>REQUIRED</specify>
      </PGField>
      <PGField>
        <typeName>bytes</typeName>
        <variableName>role1</variableName>
        <specify>REQUIRED</specify>
      </PGField>
      <PGField>
        <typeName>bytes</typeName>
        <variableName>role2</variableName>
        <specify>REQUIRED</specify>
      </PGField>
      <PGField>
        <typeName>bytes</typeName>
        <variableName>role3</variableName>
        <specify>REQUIRED</specify>
      </PGField>
      <PGField>
        <typeName>bytes</typeName>
        <variableName>role4</variableName>
        <specify>REQUIRED</specify>
      </PGField>
      <PGField>
        <typeName>uint32</typeName>
        <variableName>selectSlot</variableName>
        <specify>REQUIRED</specify>
      </PGField>
    </Fields>
    </PGData>
可以得知LoadAccountData有此几个字段:account, role1, role2, role3, role4, selectSlot.
即账户名 角色1 角色2 角色3  角色4 当前选择的角色下标 

13. Master处理gate发过来的 RPCT2M_LoginRequestNew的消息的方法:
void RpcT2M_LoginRequestNew::OnCall(const LoginRequestNewArg &roArg, LoginRequestNewRes &roRes)
{
KKSG::ErrorCode nErrCode = CLoginControl::Instance()->BeginLogin(roArg.openid(), roArg.token(), m_sessionID);
if(nErrCode != KKSG::ERR_SUCCESS)
{
roRes.set_result(nErrCode);
return;
}


CLoginRequest* poRequest = CLoginControl::Instance()->GetLoginReq(roArg.openid());
if(poRequest == NULL)
{
roRes.set_result(KKSG::ERR_STATE_ERROR);
return;
}


poRequest->m_dwRpcDelayed = DelayRpc();
nErrCode = CLoginControl::Instance()->StartTokenVerify(poRequest);
if(nErrCode != KKSG::ERR_SUCCESS)
{
CUserData oUser((UINT32)nErrCode, NULL);
CRpc::ReplyDelayRpc(poRequest->m_dwRpcDelayed, oUser);
CLoginControl::Instance()->CancelLogin(m_sessionID);
return;
}
}
简化流程就是:
KKSG::ErrorCode nErrCode = CLoginControl::Instance()->BeginLogin(roArg.openid(), roArg.token(), m_sessionID);
CLoginRequest* poRequest = CLoginControl::Instance()->GetLoginReq(roArg.openid());
nErrCode = CLoginControl::Instance()->StartTokenVerify(poRequest);
其中 StartokenVerfiy流程比较特殊,在此流程中,master会向login发送消息:RpcM2I_LoginVerfiyNew, 如下:
RpcM2I_LoginVerfiyNew* rpc = RpcM2I_LoginVerfiyNew::CreateRpc();
rpc->m_oArg.set_uid(poReq->m_qwSessionID);
rpc->m_oArg.set_logintoken(poReq->m_strToken);
rpc->m_oArg.set_serverid(MSConfig::Instance()->GetServerID());
再看看login是如何处理这个消息的:【loginserver rpcm2i_loginverifynew.cpp】
void RpcM2I_LoginVerfiyNew::OnCall(const LoginVerifyArg &roArg, LoginVerifyRes &roRes)
{
roRes.set_userid("");


UToken token;
if (!UToken::FromString(token, roArg.logintoken().data(), roArg.logintoken().size()))
{
LogWarn("db verify logintoken error: size = %d", roArg.logintoken().size());
return;
}


UINT32 serverID = LoginConfig::Instance()->GetServerID();
if (token.data[4] != serverID)
{
LogWarn("db verify send logintoken to wrong loginserver: token serverID: %d, here serverID: %d", 
token.data[4], serverID);
return;
}


char buf[32];
token.Format(buf, 32);
LogInfo("db verify logintoken [%s]", buf);


UserInfo *pUser = TokenVerifyMgr::Instance()->FindUserInfo(token);
if (pUser == NULL || pUser->isused)
{
LogWarn("db verify not found token user");
return;
}


LogInfo("db verify find token user: %s", pUser->userid.c_str());
roRes.set_userid(pUser->userid);
roRes.set_isgm(pUser->isgm);
}



















   


   
   

0 0
原创粉丝点击