前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >游戏服务器之多线程发送(上)

游戏服务器之多线程发送(上)

作者头像
李海彬
发布2018-03-22 16:37:25
8600
发布2018-03-22 16:37:25
举报
文章被收录于专栏:Golang语言社区

本文讨论的游戏架构设计中,分为两进程(逻辑服务器进程和数据服务器进程),其中逻辑服务器进程包含多个逻辑网关,单个逻辑网关中含4类线程,发送线程是其中一种并在一个逻辑网关中存在多个,用来处理发送业务。

设计上:

(1)每个玩家有对应的发送线程(N:1,根据发送线程数量哈希取余),发送时需要把数据包提交到该线程的发送添加队列里。 (2)每个玩家有个对应的网关指针 (3)分配发送线程时,会根据网关用户索引(这里的用户索引是会话索引,是会话列表中该会话的下标),获取逻辑网关上的会话列表中的会话。 (4)网关用户索引取余该逻辑网关上的发送线程数,从发送线程列表获取发送线程,并把发送包提交到该发送线程的消息队列。 (5)每个发送线程有个单独的数据包内存池。逻辑线程和每个发送线程交换数据时,每个发送线程有个单独的互斥量。数据包内存池的分配和回收都需要加锁(因为逻辑线程和发送线程之间的数据包内存池管理需要互斥)。

1、申请发送数据包

申请发送数据包,需要从逻辑网关的指定发送线程的内存池里获取。

在逻辑网关里分配发送数据包,根据该用户的网关会话索引就可以获取该玩家的会话,再哈希获取发送线程数组中的发送线程。需要设置该包的发送线程、用户索引、验证码。

代码语言:javascript
复制
inline CGateSendPacket& allocSendPacket() { return m_pGate->AllocSendPacket(m_nGateUserIndex); }  
 
CGateSendPacket& ExecSockDataMgr::AllocSendPacket(const int nUserIndex)//nUserIndex 是网关里的用户的索引 
{  
    PRUNGATEUSERSESSION pSession = m_SessionList[nUserIndex];//每个逻辑网关有一个会话列表,每个玩家相对应的会有一个用户索引 
    assert(pSession != NULL);  
    PEXECDATASENDTHREAD pSendThread = &m_SendThreads[nUserIndex % m_nSendThreadCount];//哈希发送线程(发送线程的个数是配置的,目前单逻辑网关有2个发送线程,因为游戏服务器发送的数据比接受的数据要多些) 
    CGateSendPacket *pPacket = pSendThread->pSendPacketPool->allocPacket();  
    pPacket->setSendInfo(pSendThread, nUserIndex, pSession->nVerifyIdx);//设置发送线程指针 
 return *pPacket;  
}  

数据发送线程中的数据缓存结构

代码语言:javascript
复制
typedef struct structExecDataSendThread  
{  
    ...  
 int nThreadIdx; //线程id 
    CRITICAL_SECTION SendQueueLock;//互斥锁(临界区) 
    CBList<CGateSendPacket*> *pSendAppendList;//发送添加列表 
    CBList<CGateSendPacket*> *pSendProcList;//发送处理列表 
    CGateSendPacketPool *pSendPacketPool;//发送数据包池 
}EXECDATASENDTHREAD, *PEXECDATASENDTHREAD;  

2、逻辑线程提交发送包

在提交后会把该数据包提交到该发送线程的数据包添加队列里。

代码语言:javascript
复制
CGateSendPacket &pack = ((CPlayer*)viewList->pEntity)->allocSendPacket();  
pack.writeCmd(sysBattle, sStartRun);  
pack << data;//写入字节流 
pack.flush();  
//提交网络数据包到发送队列 
inline void flush()  
{  
 if (m_pSendThread)  
    {  
        EnterCriticalSection( &m_pSendThread->SendQueueLock );  
        m_pSendThread->pSendAppendList->add(this);//添加到该线程的发送添加队列 
        LeaveCriticalSection( &m_pSendThread->SendQueueLock );  
        m_pSendThread = NULL;//置空避免重复提交 
    }  
}  
3、发送数据线程例程
把发送队列的数据拷贝到发送缓冲区。
发送发送缓冲区中的数据。
发送时需要验证发送包的验证码(nVerifyIdx)跟会话的验证码是否是一样的(发送的验证码(主要适用于服务器之间的连接的安全验证,对于客户端的连接可考虑去掉)。

[cpp] view plain copy 
void *ExecSockDataMgr::SendDataProcessRoutine(PEXECDATASENDTHREAD pRunThread)  
{  
    TICKCOUNT dwProcStartTick;  
    ExecSockDataMgr *pRunData;  
 int nLockSendBufQLockFail = 0;  
    pRunData = pRunThread->pRunData;  
 while ( !pRunData->m_boStoping )  
    {  
        dwProcStartTick = _getTickCount();  
 if ( pRunData->CopyWaitSendBuffers( pRunThread, TRUE ) )//交换发送队列和添加队列,拷贝会话的发送队列的数据到会话的发送缓冲区 
        nLockSendBufQLockFail = 0;  
 else nLockSendBufQLockFail++;  
        pRunData->CheckSendSessionBuffers( pRunThread );//发送会话的的发送缓冲区的数据 
 //循环小于指定时间则休眠一次(16ms),避免频繁io发送(发送可缓存在队列和发送缓存中) 
        pRunThread->dwProcTick = _getTickCount() - dwProcStartTick;  
 
 if ( pRunThread->dwProcTick < 16 )  
        {  
            dwProcStartTick = _getTickCount();  
            moon::OS::osSleep( 1 );  
        }  
    }  
    ExitThread( 0 );  
}  

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、申请发送数据包
  • 2、逻辑线程提交发送包
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档