登录流程

根据上一篇文章中的登录流程,我在工程中找到了相应的代码并记录了下来

登录服务代码解析

监听端口: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);
	...
}

--完--