游戏服务器的逻辑服务器下有逻辑网关(逻辑网关可有多个),逻辑网关下有一个监听线程、一个接收线程、一个接收处理线程和多个发送线程。
所以,主线程、监听线程、接收线程、接收处理线程、发送线程之间需要对会话的数据进行管理。
设计上:
(1)在主线程的所有的网关对象管理会话列表的初始化和销毁,在启动网关时会初始化。
(2)监听线程监听到新的连接会创建会话(分配发送缓冲区和接收缓冲区),并添加到会话列表。
(3)接收数据处理线程遍历会话列表,标识关闭长时间(10min)不活动的会话。对于被标识关闭(可能在别的线程标识的)的会话需要释放会话的资源(接收缓冲区和发送缓冲区),并且置空该会话在会话列表中的位置(以便以后放置新的会话)。
(4)发送线程关闭发送错误的会话(并标识关闭),关闭发送包过大(4MB)的会话。
(5)接收线程关闭接收出错的会话(以及标记关闭的会话)。
1、网关会话列表初始化和销毁(主线程)
(1)网关启动初始化会话列表
BOOL ExecSockDataMgr::Start()
{
......
//初始化会话队列
InitSessions();
......
}
VOID ExecSockDataMgr::InitSessions()
{
EnterCriticalSection( &m_SessionLock );
m_SessionList.reserve(65536);
LeaveCriticalSection( &m_SessionLock );
}
(2)网关关闭,关闭所有的会话
关闭和销毁会话列表里的所有的会话,回收其内存
VOID ExecSockDataMgr::Stop()
{
......
CancelRemainSendSessionBuffers();
CloseAllSessions( TRUE );//关闭所有的会话
UninitSessions();//释放所有的会话
m_boStarted = FALSE;
}
释放该网关中所有的会话
VOID ExecSockDataMgr::UninitSessions()
{
EnterCriticalSection( &m_SessionLock );
PRUNGATEUSERSESSION *pSessionList = m_SessionList;
PRUNGATEUSERSESSION pSession;
for (INT_PTR i=m_SessionList.count()-1; i>-1; --i)
{
pSession = pSessionList[i];
if (pSession)
{
FreeSession(pSession);
}
}
m_SessionList.clear();
m_SessionAllocator.freeAll();
LeaveCriticalSection( &m_SessionLock );
}
关闭所有的会话
VOID ExecSockDataMgr::CloseAllSessions(BOOL boForceClose)
{
INT_PTR i;
TICKCOUNT dwCurTick;
PRUNGATEUSERSESSION pSession;
EnterCriticalSection( &m_SessionLock );//会话锁
EnterCriticalSection( &m_RecvQueueLock );//接收队列锁(单个网关内接受数据线程有一个)
for ( i=0; i<m_nSendThreadCount; ++i )
{
EnterCriticalSection( &m_SendThreads[i].SendQueueLock );// 发送队列锁(单个网关内发送线程有多个)
}
moon::OS::osSleep(3);//休眠以等待数据接收以及各发送线程被阻塞
dwCurTick = _getTickCount();
for ( i=m_SessionList.count()-1; i>-1; --i )
{
pSession = m_SessionList[i];
if ( pSession && pSession->nSocket != INVALID_SOCKET )
{
if ( boForceClose )
{
m_SessionList[i] = NULL;
PostEngineClosePlayer( pSession );
closesocket( pSession->nSocket );
pSession->boMarkToClose = true;
pSession->boRemoteClosed = true;
pSession->dwCloseTick = 0;
pSession->nSocket = INVALID_SOCKET;
pSession->nVerifyIdx = 0;
FreeSession(pSession);
}
else CloseSession( pSession );//关闭会话(关闭socket和标识关闭会话)
}
}
for ( i=0; i<m_nSendThreadCount; ++i )
{
LeaveCriticalSection( &m_SendThreads[i].SendQueueLock );
}
LeaveCriticalSection( &m_RecvQueueLock );
LeaveCriticalSection( &m_SessionLock );
}
2、创建会话(监听线程)
添加会话到会话列表
PRUNGATEUSERSESSION ExecSockDataMgr::NewSession(SOCKET nSocket, SOCKADDRIN RemoteAddr)
{
PRUNGATEUSERSESSION