游戏服务器之被动连接线程池

来源:互联网 发布:sql declare 全局变量 编辑:程序博客网 时间:2024/04/23 14:51

本文内容是游戏服务器之被动连接线程池。顾名思义,被动连接线程池处理的是被动连接的会话的状态管理和数据收发。


设计上:

每个服务器对象有个接收连接线程池, 线程类型包括:

验证线程、同步线程、网络接收发送线程、回收线程。

会话正常流程步骤(正常状态切换流程):

(1)程序主线程接收连接(监听连接)

(2)验证线程(检查验证超时、验证发来消息的服务器id和服务器ip(服务器之间,依靠中心服务器发来的依赖服务器列表)、验证账号和临时id和账号(客户端登陆和重登陆))

(3)同步线程(网关发消息删除中心服务器登陆会话,中心服务器读数据库检查其他服务器连接的合法性、发送测试消息到对端)

(4)网络接收发送线程

(5)回收线程(回收连接)


本文目录:

1、被动连接线程池初始化

2、被动连接池的会话管理

(1)未使用会话到验证线程

(2)验证会话到同步线程

(3)会话到回收线程

3、网络数据收发

(1)处理网络读

(1-1)创建读epoll描述符

(1-2)遍历所有连接

(1-3)检查读事件

(1-4)回收需要断开的连接

(1-5)添加需要读的连接到epoll描述符

(1-6)处理epoll读事件(非阻塞检查)

(1-7)接收有读事件的连接的数据

(2)处理网络写

(2-1)成功接收到数据才检查写缓存(连接的写事件一直存在)

(2-2)每隔一段时间才检查写缓存

(2-3)检查epoll描述符(kdpfd)有事件的连接(写事件一直存在)

(2-4)再次是否有读事件(epoll描述符kdpfd)

(2-5)连接的接收数据

(2-6)检查连接的写事件(连接的写事件一直存在,目的检查的是写缓存)

(2-6)连接写数据

(3)进程关闭前回收所有的会话

4、服务器关闭销毁资源


本文内容:

1、被动连接线程池初始化

初始化是在进程主线程的服务器对象(具体服务器类对象)的初始化函数里。

线程类型包括:回收线程、验证线程、同步线程、网络接收发送线程(网络接收发送线程也是根据配置有多个)。


 线程池初始化

bool tcp_session_pool::init()
{
//创建回收线程
if(!recycleThreads.init(1,1,"recycle_thread",this))
{
g_log->debug("recycle_thread线程启动失败");
return false;
}
//创建初始化验证线程
if(!verifyThreads.init(1,8,"verify_thread",this))//检查验证超时、中心服务器的服务器配置验证(读数据库)
{
g_log->debug("verify_thread线程启动失败");
return false;
}
//创建初始化同步线程
if(!syncThreads.init(1,1,"sync_thread",this))
{
g_log->debug("sync_thread线程启动失败");
return false;
}
//创建初始网络接收发送线程(这里的网络接收发送线程只是处理网络接收和发送的线程)
int maxThreadCount = (maxConns + main_service_thread::getMaxSize() - 1)/main_service_thread::getMaxSize();
g_log->debug("线程最大连接数%d,每线程连接数%d,线程个数%d",maxConns,main_service_thread::getMaxSize(),maxThreadCount);
if(!okayThreads.init(1,maxThreadCount,"main_service_thread",this))
{
g_log->debug("main_service_thread线程启动失败");
return false;
}
return true;
}


2、被动连接池的会话管理

连接的状态处理是由线程池的那些状态管理线程来处理的,连接的状态会进行转换,把连接对象交给其他线程来管理。

(1)未使用会话到验证线程

bool tcp_session_pool::addVerify(tcp_session *task)
{
verify_thread *pThread = verifyThreads.getOne();
if(pThread)
{
/*
* cjy

*state_notuse -> state_sync 
* 先设置状态再添加容器,
* 否则会导致一个task同时在两个线程中的危险情况
*/
task->getNextState();
pThread->add(task);
}
else
{
g_log->error("没有找到验证线程添加任务");
}
return true;
}

(2)验证会话到同步线程

void tcp_session_pool::addSync(tcp_session *task)
{
sync_thread *pThread = syncThreads.getOne();
if(pThread)
{
// state_verify-> state_sync
/*
* cjy
* 先设置状态再添加容器
*/
task->getNextState();
pThread->add(task);
}
else
{
g_log->error("没有找到回收线程添加任务");
}
}

(2)同步会话到网络收发线程

bool tcp_session_pool::addOkay(tcp_session *task)
{
main_service_thread *pThread = okayThreads.getOne();
if (pThread)
{
// state_sync -> state_okay
/*
* cjy
* 先设置状态再添加容器,
*/
task->getNextState();
pThread->add(task);
return true;
}

g_log->fatal("没有找到主线程添加任务");
//没有找到线程来处理这个连接,需要回收关闭连接
return false;
}

(3)会话到回收线程

void tcp_session_pool::addRecycle(tcp_session *task)
{
recycle_thread *pThread = recycleThreads.getOne();
if(pThread)
{
pThread->add(task);
}
else
{
g_log->error("没有找到回收线程添加任务");
}
}

3、网络数据收发

网络收发线程的循环部分,处理网络数据的接收和发送。

网络的处理是读优先的,有读才检查写,写的检查是一段时间检查一次。

有个读epoll描述符来处理网络数据的接收。

void main_service_thread::run()
{
realtime currentTime;
realtime _write_time;
tcp_session_IT it;

(1)处理网络读
int kdpfd_r;
vector_epoll epfds_r;

(1-1)创建读epoll描述符
kdpfd_r = epoll_create(256);//创建读epoll描述符
assert(-1 != kdpfd_r);
epfds.resize(256);
uint32 fds_count_r = 0;
bool check=false;
while(!isFinal())
{
this->setRunning();
currentTime.now();

if (check)
{
check_queue();
if (!tasks.empty())
{

(1-2)遍历所有连接
for(it = tasks.begin(); it != tasks.end();)//遍历所有连接
{
tcp_session *task = *it;
if(task->mSocket.checkChangeSocket())//检查是否需要更换套接字,需要的就先从epoll描述符删除再添加
{

(1-3)检查读事件
if (task->isFdsrAdd())
{
task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);
}
task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);
task->mSocket.changeSocket(0);
if (task->isFdsrAdd())
{
task->addEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI, (void *)task);
}
task->addEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI, (void *)task);//kdpfd 里面一直有所有的连接
}

//检查测试信号指令(检查是否需要发送网络测试信号(只有简单包头的没有内容的消息)到客户端)
task->checkSignal(currentTime);
(1-4)回收需要断开的连接
if (task->isTerminateWait())//检查任务是否被中断,是的话就要设置中断任务标识
{
task->Terminate();
}
if (task->isTerminate())//如果连接被中断就从epoll中删除(从epoll描述符中删除掉对该套接字的监听),并回收该连接
{
if (task->isFdsrAdd())
{
task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);
fds_count_r --;
}
remove(it);
 task->getNextState();//先设置状态再添加容器
pool->addRecycle(task);
}
else
{

(1-5)添加需要读的连接到epoll描述符
if(!task->isFdsrAdd())
{
task->addEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI, (void *)task);
task->fdsrAdd();//需要加入一个读事件到事件列表
fds_count_r++;
if (fds_count_r > epfds_r.size())
{
epfds_r.resize(fds_count_r + 16);
}
}
++it;
}
}
}
check=false;
}
thread_base::msleep(2);

(1-6)处理epoll读事件(非阻塞检查)
if(fds_count_r)//处理epoll读事件
{
int retcode = epoll_wait(kdpfd_r, &epfds_r[0], fds_count_r, 0);
if (retcode > 0)
{
for(int i = 0; i < retcode; i++)
{
tcp_session *task = (tcp_session *)epfds_r[i].data.ptr;
if (epfds_r[i].events & (EPOLLERR | EPOLLPRI))
{
//套接口出现错误(套接字出现错误就中断该任务)
if(task->TerminateError())
{
g_log->debug("%s: 套接口异常错误:%u", __PRETTY_FUNCTION__,epfds_r[i].events);
task->Terminate(tcp_session::terminate_active);
check=true;
}
else
{
if (task->isFdsrAdd())//从epoll描述符中删除该套接字
{
task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);
}
task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);
}
}
else
{

(1-7)接收有读事件的连接的数据
if (epfds_r[i].events & EPOLLIN)//处理读取事件
{
//套接口准备好了读取操作
if (!task->ListeningRecv(true))//处理网络读取事件,读取网络缓冲区的数据到指令,并分析指令
{//读取接收缓冲区数据如果返回-1则是对方已经断开连接,则要中断任务
g_log->debug("%s: 套接口读操作错误 errno:%u, strerror:%s", __PRETTY_FUNCTION__, errno, strerror(errno));
task->Terminate(tcp_session::terminate_active);
check=true;
}
}
}
epfds_r[i].events=0;
}
}
}

(2)处理网络写

(2-1)成功接收到数据才检查写缓存(连接的写事件一直存在)
if(check)//套接字出错或者有读事件但还没到读缓冲区则继续读下一个,否则开始检查写缓存(连接的写事件一直存在)
{
continue;
}

(2-2)每隔一段时间才检查写缓存

//每隔一段时间才查看检查是否有写事件需要处理
if (currentTime.msec() - _write_time.msec() >= (unsigned long)(pool->usleep_time/1000))
{
_write_time = currentTime;
if (!tasks.empty())
{

(2-3)检查epoll描述符(kdpfd)有事件的连接(写事件一直存在)
int retcode = epoll_wait(kdpfd, &epfds[0], task_count, 0);//处理其他服务器的连接(对于场景服务器,只有网关连接过来)
if (retcode > 0)
{
for(int i = 0; i < retcode; i++)
{
tcp_session *task = (tcp_session *)epfds[i].data.ptr;
if (epfds[i].events & (EPOLLERR | EPOLLPRI))//错误则中断任务
{
//套接口出现错误
if(task->TerminateError())
{
g_log->debug("%s: 套接口异常错误:%u", __PRETTY_FUNCTION__,epfds[i].events);
task->Terminate(tcp_session::terminate_active);
}
else
{
if (task->isFdsrAdd())
{
task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);
}
task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);
}
}
else
{

(2-4)再次是否有读事件(epoll描述符kdpfd)
if (epfds[i].events & EPOLLIN)//这里又进行一次读事件检查,是为了尽量多处理事件,这样可以尽量减少系统调用从而提高效率(epoll使用的是水平事件,就算这里不处理读事件也是可以的,但是还是尽量多处理掉多些事件来减少epoll_wait的调用)
{

(2-5)连接的接收数据
//套接口准备好了读取操作
if (!task->ListeningRecv(true))
{
g_log->debug("%s: 套接口读操作错误,errno:%u, strerror:%s", __PRETTY_FUNCTION__,errno, strerror(errno));
task->Terminate(tcp_session::terminate_active);
}
}

(2-6)检查连接的写事件(连接的写事件一直存在,目的检查的是写缓存)
if (epfds[i].events & EPOLLOUT)//处理写事件
{

(2-6)连接写数据
//套接口准备好了写入操作
if (!task->ListeningSend())
{
g_log->debug("%s: 套接口写操作错误errno:%u,strerrno:%s", __PRETTY_FUNCTION__,errno,strerror(errno));
task->Terminate(tcp_session::terminate_active);
}
}
}
epfds[i].events=0;//该事件处理完后去掉该标识
}
}
}
check=true;
}
}
(3)进程关闭前回收所有的会话
回收所有的会话:
//线程关闭后就先要加入回收队列先把所有的任务回收掉
//把所有任务队列中的连接加入到回收队列中,回收这些连接
for(it = tasks.begin(); it != tasks.end();)
{
tcp_session *task = *it;
remove(it);
// state_sync -> state_okay
/*
* cjy
* 先设置状态再添加容器,
*/
task->getNextState();
pool->addRecycle(task);
}
TEMP_FAILURE_RETRY(::close(kdpfd_r));//关闭epoll描述符
}

 

4、服务器关闭销毁资源
server_base::~server_base()
{
    if (taskPool)//回收连接池,关闭连接管理和收发的线程
{
taskPool->final();
SAFE_DELETE(taskPool);
}

//关闭epoll句柄
    TEMP_FAILURE_RETRY(::close(epoll_handler));
if (-1 != sock_handler) 
{
::shutdown(sock_handler, SHUT_RD);
TEMP_FAILURE_RETRY(::close(sock_handler));
sock_handler = -1;
}
}


连接池删除时需要关闭所有这些线程

void tcp_session_pool::final()
{
g_log->debug("%s", __PRETTY_FUNCTION__);
verifyThreads.final();
syncThreads.final();
okayThreads.final();
recycleThreads.final();
}


转自:http://blog.csdn.net/chenjiayi_yun/article/details/17793459

0 0
原创粉丝点击