您的位置:首页 > 其它

游戏服务器之服务器之间的主动连接

2014-02-10 01:24 281 查看
游戏服务器之服务器之间的主动连接的设计和实现,本文的代码内容是以自定义主动连接socket来体现。

设计上:

创建主动连接socket线程,功能如下

(1)处理主动连接到其他服务器

(2)数据的收发

(3)发送心跳

1、自定义客户端连接socket启动

创建连接socket线程。

BOOL CCustomClientSocket::Startup()
{
if ( TRUE == InterlockedCompareExchange(&m_boStoped, FALSE, TRUE) )
{
#if PLATFORM == WINDOWS32 || PLATFORM == WINDOWS64
m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CustomClientSocketWorkRoutine, this, 0, &m_dwThreadId);
if ( !m_hThread )
{
logError("创建%s通信线程失败,%d", m_sClientName, GetLastError());
return FALSE;
}
#endif
#if PLATFORM == LINUX32 || PLATFORM == LINUX64
if(0 != pthread_create(&m_hThread, 0, (void *(*)(void*))(&CustomClientSocketWorkRoutine), this))
{
logError("创建%s通信线程失败,%d", m_sClientName,errno);
return false;
}
#endif
}
return TRUE;
}


2、创建主动连接线程

(1)性能检测和周期控制

不断执行socket线程例程,控制周期,并计算执行例程的性能统计。
周期控制:

1) 每次循环最多调用两次例程socket线程的函数,最大循环时间4ms(不足的时间会在当次循环末尾休眠该时间)
性能统计:
1)每隔60s重新统计一次

2)记录循环次数(最大和最小)

3)记录收发时间(最大和最小)

4)记录休眠时间(最大和最小)

VOID STDCALL CCustomClientSocket::CustomClientSocketWorkRoutine(CCustomClientSocket *lpClient)
{
static const int MaxLoop = 2;//最大循环次数
static const TICKCOUNT MaxCPU = 4;//最大循环时间
TICKCOUNT dwStartTick, dwCurTick, dwRestaticTick = 0;
int nLoopCount;
DWORD dwSleepTick;

lpClient->OnWorkThreadStart();

while ( !lpClient->m_boStoped )
{
lpClient->m_MainProcPerformance.dwTickBegin = dwStartTick = _getTickCount();//开始性能计算时间
//是否重新统计循环性能(每隔60s重新统计)
if ( dwStartTick >= dwRestaticTick )
{
dwRestaticTick = dwStartTick + 60 * 1000;
lpClient->m_LoopPerformance.nMaxLoop = 0;
lpClient->m_LoopPerformance.nMinLoop = 99999;

lpClient->m_MainProcPerformance.dwMaxTick = 0;
lpClient->m_MainProcPerformance.dwMinTick = 9999;
}

nLoopCount = 0;
while ( TRUE )
{
lpClient->SingleRun();//客户端连接例程(处理数据收发和心跳)
nLoopCount++;
dwCurTick = _getTickCount();//当前时间

if ( dwCurTick - dwStartTick >= MaxCPU )//最大循环时间(4ms)
{
dwSleepTick = 1;
break;
}
if ( nLoopCount >= MaxLoop )//最大循环次数2
{
////每次循环时间4ms ,没使用完的时间就休眠
dwSleepTick = (DWORD)(MaxCPU - (dwCurTick - dwStartTick));
//如果实际休眠值为此值则说明上一步的运算溢出了
if ( dwSleepTick > MaxCPU + 2 ) dwSleepTick = MaxCPU + 2;
break;
}
}

//循环次数
lpClient->m_LoopPerformance.nLastLoop = nLoopCount;
//收发使用时间
lpClient->m_MainProcPerformance.dwLastTick = dwCurTick - lpClient->m_MainProcPerformance.dwTickBegin;
//休眠时间
lpClient->m_MainSleepPerformance.dwLastTick = dwSleepTick;
//记录循环次数(最大最小)
if ( lpClient->m_LoopPerformance.nMaxLoop < nLoopCount )
{
lpClient->m_LoopPerformance.nMaxLoop = nLoopCount;
}
if ( lpClient->m_LoopPerformance.nMinLoop > nLoopCount )
{
lpClient->m_LoopPerformance.nMinLoop = nLoopCount;
}
//记录收发时间(最大最小)
if ( lpClient->m_MainProcPerformance.dwMaxTick < lpClient->m_MainProcPerformance.dwLastTick )
{
lpClient->m_MainProcPerformance.dwMaxTick = lpClient->m_MainProcPerformance.dwLastTick;
}
if ( lpClient->m_MainProcPerformance.dwMinTick > lpClient->m_MainProcPerformance.dwLastTick )
{
lpClient->m_MainProcPerformance.dwMinTick = lpClient->m_MainProcPerformance.dwLastTick;
}
//记录休眠时间(最大最小)
if ( lpClient->m_MainSleepPerformance.dwMaxTick < dwSleepTick )
{
lpClient->m_MainSleepPerformance.dwMaxTick = dwSleepTick;
}
if ( lpClient->m_MainSleepPerformance.dwMinTick > dwSleepTick )
{
lpClient->m_MainSleepPerformance.dwMinTick = dwSleepTick;
}
moon::OS::osSleep(dwSleepTick);
}

if ( lpClient->connected() )
{
lpClient->close();
}

lpClient->OnWorkThreadStop();

ExitThread(0);//设置线程退出返回值
}


(2)自定义客户端连接socket例程

在例程中保持心跳消息(保持长连接)。

VOID CCustomClientSocket::SingleRun()
{
// TICKCOUNT dwCurTick = _getTickCount();
//连接到服务器
if ( !connected() )
ConnectToServer();//创建连接socket并连接

super::SingleRun();

//发送保持连接消息
if ( connected() )
{
if ( _getTickCount() - m_dwMsgTick >= 10 * 1000 )
{
SendKeepAlive();//10s 一次心跳包
}
}
}


(3)创建连接socket并连接

创建socket(主动连接socket对象)并主动连接到服务器,设置socket属性。

BOOL CCustomClientSocket::ConnectToServer()
{
int nErr;

//如果已连接则直接返回
if ( connected() )
{
return TRUE;
}

TICKCOUNT dwCurTick = _getTickCount();

//创建套接字
if ( getSocket() == INVALID_SOCKET )
{
SOCKET sock;
nErr = createSocket(&sock);
if ( nErr )
{
logError("创建%s客户端套接字失败,error %s %d", m_sClientName,nErr);
return FALSE;
}
m_nSocket = sock;
//调整发送和接收缓冲大小
nErr = setSendBufSize(32 * 1024);
if ( nErr )
{
logError("调整%s套接字发送缓冲长度失败 %s %d", m_sClientName,nErr);
return FALSE;
}
nErr = setRecvBufSize(32 * 1024);
if ( nErr )
{
logError("调整%s套接字接收缓冲长度失败,%d", m_sClientName,nErr);
return FALSE;
}
}
//连接到会话服务器
if ( dwCurTick >= m_dwReconnectTick )
{
m_dwReconnectTick = dwCurTick + 5 * 1000;
nErr = connect(m_sSrvHost,(INT) m_nSrvPort);
if ( nErr )
{
logError("连接到%s服务器失败", m_sClientName );
return FALSE;
}
//调整为非阻塞模式
nErr = setBlockMode(FALSE);
if ( nErr )
{
logError("调整%s套接字接为非阻塞模式失败,%d", m_sClientName,nErr);
return FALSE;
}
m_dwConnectTick = m_dwMsgTick = dwCurTick;
return TRUE;
}

return FALSE;
}


(4)连接socket收发数据

接收数据、执行例程、发送数据。

对每项业务处理需要先判断连接是否正常。

VOID CCustomWorkSocket::SingleRun()
{
//接收数据
if ( connected() )
ReadSocket();

//处理接受到的数据包
if ( connected() )
ProcessRecvBuffers(m_pProcRecvBuffer);

//调用例行函数
OnRun();

//发送数据
if ( connected() && m_bSendData)
{
SendSocketBuffers();
}
}


3、自定义客户端连接socket关闭

设置关闭标识、关闭线程、清理发送缓冲区和线程id

VOID CCustomClientSocket::Stop()
{
if ( FALSE == InterlockedCompareExchange(&m_boStoped, TRUE, FALSE) )
{
CloseThread(m_hThread);//关闭线程(等待回收子线程线程并取消线程)  #define CloseThread(ht){ waitpid( ht, 0, 0 ); pthread_cancel( ht ); }
ClearSendBuffers();
m_dwThreadId = 0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: