LastBattle | 登录流程(LS & BS & GS)
登录流程
根据上一篇文章中的登录流程,我在工程中找到了相应的代码并记录了下来
登录服务代码解析
监听端口:49997 供BS服务连接
监听端口:49996 供客户端连接
CIocpCtrl:IOCP控制类
多线程循环查询IOCP内部的网络事件,并分派处理.这里多个工作线程仅仅是将所有的网络事件放入循环
内部将创建2 * CPU个工作线程(Worker Item).
多线程调用函数
- 新建客户端连接 poListener->OnAccept(bRet, pstPerIoData);- 接受消息 poSock->OnRecv(dwByteTrabsferred);- 发送消息 poSock->OnSend(dwByteTrabsferred);
void OnExecute()
{
CCPSock* poSock;
//代表一个套接字类
CCpListener* poListener; //IOCP监听器
...
//当有客户端请求创建连接时
if(pstPerHandleData->bListen)
{
poListener->OnAccept(bRet, pstPerIoData);
}
else
{
poSock = (CCPSock*)pstPerHandleData->ptr;
···
switch(pstPerIoData->nOp)
{
case IOCP_RECV:
//接受数据
{
poSock->DecPostRecv();
if (dwByteTrabsferred > 0)
{
poSock->OnRecv(dwByteTrabsferred);
}
else
{
INFO(_SDT("[%s:%d]CCPSock connID=%d error %d, close it, socket :%d "),
MSG_MARK, poSock->GetConnectionID(), ::WSAGetLastError(), poSock->GetSock());
poSock->OnClose();
}
}
break;
case IOCP_SEND:
//发送数据
{
poSock->DecPostSend();
if (dwByteTrabsferred > 0)
{
poSock->OnSend(dwByteTrabsferred);
}
else
{
INFO(_SDT("[%s:%d]CCPSock connID=%d error %d, close it"),
MSG_MARK, poSock->GetConnectionID(), ::WSAGetLastError());
poSock->OnClose();
}
}
break;
case IOCP_CLOSE:
{
poSock->OnClose(false);
}
break;
default:
;
}
}
...
}
CCpListener: IOCP监听器
IOCP控制类在有新客户端连接上来时调用监听器的OnAccept函数
void CCpListener::OnAccept(BOOL bSucc, SPerIoData* pstPerIoData)
{
...
CUCConnection* poConnection = & pConnData->connection;
//创建连接对象
ISDSession* poSession = m_poSessionFactory->CreateSession(poConnection);
poConnection->SetSession(poSession);
...
//
// 应该先投递连接事件再关联套接口,否则可能出现第一个Recv事件先于连接事件入队列
//
//CEventMgr::Instance()->PushEstablishEvt(poConnection, true, m_dwID);
if(false == poSock->AssociateWithIocp())
{
poSock->DoClose();
}
else
{
//从内核中拷贝数据到内存中
if(false == poSock->PostRecv())
{
poSock->DoClose();
}
}
}
CCPSock:套接字类
在CCPSocket::PostRecv();接收数据到内存, 并且接受数据计数+1
bool CCPSocket::PostIRecv()
{
...
if (0 != WSARecv(m_hSock, &m_stRecvIoData.stWsaBuf, 1, &dwReadLen, &dwFlags, &m_stRecvIoData.stOverlapped, NULL))
{
int errNo = WSAGetLastError();
if (errNo != WSA_IO_PENDING)
{
WARN(_SDT("[%s:%d]post WSARecv failed, errNo=%d, %p "), MSG_MARK, errNo, m_pRecvBuf);
return false;
}
}
IncPostRecv(); // 接受消息计数+1
...
}
CCPSock::OnRecv(DWORD dwBytes) 添加一个完整的数据包到接收数据事件中
void CCPSock::OnRecv(DWORD dwBytes)
{
...
CEventMgr::Instance()->PushRecvEvt(m_pConnData, GetConnectionID(), p, nUsed);
...
}
INetSessionMgr:网络会话管理类
主线程定时调用 INetSessionMgr::GetInstance()->Update();
void INetSessionMgr::Update()
{
mNetModule->Run();
...
}
IUCNet* mNetModule : 网络模块
获取 NETEVT_RECV 接收数据事件
bool CUCODENetWin::Run(INT32 nCount)
{
...
switch(stEvent.nType)
{
case NETEVT_RECV:
_ProcRecvEvt(&stEvent.stUn.stRecv);
break;
...
}
CEventMgr::Instance()->ReleaseNetEvt(pstEvent);
}
void CUCODENetWin::_ProcRecvEvt(SRecvEvt* pstEvent)
{
...
pConnData->connection.OnRecv(m_pRecvBuf, pstEvent->nLen);
...
}
CClientSession : 客户端会话类
客户端连接类 CClientSession 收到数据回调
void CUCConnection::OnRecv(const char* pData, INT32 nLen)
{
if(m_nConnStat != CONN_ASSOCIATE)
{
return;
}
SDASSERT(m_poSession != NULL);
m_poSession->OnRecv(pData, nLen); //回调
}
ISDSessionm_poSession 基类指针指向子类 CClientSession
回调到 类 INetSession中,因为 CClientSession 是 INetSession子类
void UCAPI INetSession::OnRecv(const char* pBuf, UINT32 dwLen){
···
bool bRet = pNode->mHandle(pMsgData, n32MsgLen, this, pNetHeader->type);
···
}
在类 CClientSession 中 收到第1消息:请求登录,放入登录队列
bool CClientSession::Msg_Handle_Init(const char* pMsg, int n32MsgLength, INetSession* vthis, int n32MsgID)
{
// 收到第1消息:请求登录,放入登录队列
boost::shared_ptr<GCToLS::AskLogin> sLogin = ParseProtoMsg<GCToLS::AskLogin>(pMsg, n32MsgLength);
if (!sLogin){
//ELOG(LOG_ERROR, "Login Fail With Msg Analysis Error.");
SDKAsynHandler::GetInstance().PostToLoginFailQueue(eEC_TBInvalidToken, vthis->GetID());
return 0;
}
SDKAsynHandler::GetInstance().CheckLogin(*sLogin, vthis->GetID());
vthis->SetInited(true,true);
return true;
}
int SDKAsynHandler::CheckLogin(GCToLS::AskLogin& sAskLogin, int gcnetid){
...
switch(un32platform)
{
case ePlatform_PC:
sSendData = "PCTest";
break;
}
// 生产消息,往队列 m_SDKCallbackQueue 里加数据
PostMsg(sSendData.c_str(), sSendData.size(), sAskLogin.msgid(), gcnetid, (EUserPlatform)un32platform);
}
生产消费者模型1
通过 PostMsg()函数生产消息,往队列 m_SDKCallbackQueue 里加数据
void PostMsg(const char* pMsg, int length, int msgid, int gcnetid, EUserPlatform eplat);
在一个线程里,启动一个定时器 newtimer_cb,消费队列 m_SDKCallbackQueue
生产消费者模型2
SendToInsertData()函数生产消息 推送到队列 m_DBCallbackQueue
SdkConnector::GetInstance().SendToInsertData(sUserData.uin, sTempInfo, gcnetid);
主线程,循环消费 m_DBCallbackQueue
SdkConnector::GetInstance().Update();
登录成功, 给客户端推送BS服务器列表
void SdkConnector::Update(){
...
PostMsgToGC_NotifyServerList(gcnetid);
...
}
--完--
- 原文作者: 留白
- 原文链接: https://zfunnily.github.io/2020/11/lastbattlelogin/
- 更新时间:2024-04-16 01:01:05
- 本文声明:转载请标记原文作者及链接