游戏服务器之网络收发线程处理详细分析

业务网络收发线程的循环部分,处理网络数据的接收和发送。
设计上:

(1)先检查所有连接是否处于正常状态。

(2)网络的处理是读优先的,有读才检查写,写的检查是一段时间检查一次(目前是50ms)。

实现上通过两个epoll描述符来处理。
第一个epoll描述符监听所有连接的读事件,并处理网络数据的接收。

第二个epoll描述符监听所有连接的写事件(顺带会检查读事件),并处理网络数据的发送(顺带会处理有读事件的连接的接收)。


处理步骤:

(1)例行检查: 遍历所有连接,检查连接是否有读事件,发送测试信号(心跳)到客户端,断开需要断开的连接并回收

(2)检查需要检查的连接的读事件,并处理epoll读事件(非阻塞检查)

(3)每隔一段时间并且在有读事件情况下的检查是否有写缓存,若有数据则处理写事件(非阻塞检查)

  1. void main_service_thread::run()  
  2. {  
  3.     realtime currentTime;  
  4.     realtime _write_time;  
  5.     tcp_session_IT it;  
  6.   
  7.     int kdpfd_r;  
  8.     vector_epoll epfds_r;  
  9.     kdpfd_r = epoll_create(256);//创建读epoll描述符  
  10.     assert(-1 != kdpfd_r);  
  11.     epfds.resize(256);  
  12.     uint32 fds_count_r = 0;  
  13.     bool check=false;  
  14.     while(!isFinal())  
  15.     {  
  16.         this->setRunning();  
  17.         currentTime.now();  
  18.         //检查没有关闭的连接,作为读事件的检查对象  
  19.         if (check)  
  20.         {  
  21.             check_queue();  
  22.             if (!tasks.empty())  
  23.             {  
  24.                 //遍历所有连接,检查连接是否有读事件,发送测试信号(心跳)到客户端,断开需要断开的连接并回收  
  25.                 for(it = tasks.begin(); it != tasks.end();)//遍历所有连接  
  26.                 {  
  27.                     tcp_session *task = *it;  
  28.       
  29.                     //检查测试信号指令(检查是否需要发送网络测试信号(只有简单包头的没有内容的消息)到客户端)  
  30.                     task->checkSignal(currentTime);  
  31.                     //回收需要断开的连接  
  32.                     if (task->isTerminateWait())//检查任务是否被中断,是的话就要设置中断任务标识  
  33.                     {  
  34.                         task->Terminate();  
  35.                     }  
  36.                     if (task->isTerminate())//如果连接被中断就从读epoll中删除(从epoll描述符中删除掉对该套接字的监听),并回收该连接  
  37.                     {  
  38.                         if (task->isFdsrAdd())  
  39.                         {  
  40.                             task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);  
  41.                             fds_count_r --;  
  42.                         }  
  43.                         remove(it);  
  44.                         task->getNextState();//回收连接(先设置状态再添加容器)  
  45.                         pool->addRecycle(task);  
  46.                     }  
  47.                     else  
  48.                     {  
  49.                         //添加需要读的连接到epoll描述符  
  50.                         if(!task->isFdsrAdd())  
  51.                         {  
  52.                             task->addEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI, (void *)task);  
  53.                             task->fdsrAdd();//需要加入一个连接到读事件监听列表  
  54.                             fds_count_r++;  
  55.                             if (fds_count_r > epfds_r.size())  
  56.                             {  
  57.                                 epfds_r.resize(fds_count_r + 16);  
  58.                             }  
  59.                         }  
  60.                         ++it;  
  61.                     }  
  62.                 }  
  63.             }  
  64.             check=false;  
  65.         }  
  66.         thread_base::msleep(2);  
  67.       
  68.         //检查需要检查的连接的读事件,并处理epoll读事件(非阻塞检查)  
  69.         if(fds_count_r)//处理epoll读事件  
  70.         {  
  71.             int retcode = epoll_wait(kdpfd_r, &epfds_r[0], fds_count_r, 0);  
  72.             if (retcode > 0)  
  73.             {  
  74.                 for(int i = 0; i < retcode; i++)  
  75.                 {  
  76.                     tcp_session *task = (tcp_session *)epfds_r[i].data.ptr;  
  77.                     if (epfds_r[i].events & (EPOLLERR | EPOLLPRI))  
  78.                     {  
  79.                         //套接口出现错误(套接字出现错误就中断该任务)  
  80.                         if(task->TerminateError())  
  81.                         {  
  82.                             g_log->debug("%s: 套接口异常错误:%u", __PRETTY_FUNCTION__,epfds_r[i].events);  
  83.                             task->Terminate(tcp_session::terminate_active);  
  84.                             check=true;  
  85.                         }  
  86.                         else  
  87.                         {  
  88.                             if (task->isFdsrAdd())//从epoll描述符中删除该套接字  
  89.                             {  
  90.                                 task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);//这里可能导致fds_count_r大于需要监听读的连接数  
  91.                             }  
  92.                             task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);  
  93.                         }  
  94.                     }  
  95.                     else  
  96.                     {  
  97.                         //接收有读事件的连接的数据  
  98.                         if (epfds_r[i].events & EPOLLIN)//处理读取事件  
  99.                         {  
  100.                             //套接口准备好了读取操作  
  101.                             if (!task->ListeningRecv(true))//处理网络读取事件,读取网络缓冲区的数据到指令,并分析指令  
  102.                             {  
  103.                                 //读取接收缓冲区数据如果返回-1则是对方已经断开连接,则要中断任务  
  104.                                 g_log->debug("%s: 套接口读操作错误 errno:%u, strerror:%s", __PRETTY_FUNCTION__, errno, strerror(errno));  
  105.                                 task->Terminate(tcp_session::terminate_active);  
  106.                                 check=true;  
  107.                             }  
  108.                         }  
  109.                     }  
  110.                     epfds_r[i].events=0;  
  111.                 }  
  112.             }  
  113.         }  
  114.           
  115.         //成功接收到数据才检查写缓存 (一直监听连接的写事件)  
  116.         //套接字出错或者有读事件但还没到读缓冲区则继续检查读,否则开始检查写缓存  
  117.         if(check)  
  118.         {  
  119.             continue;  
  120.         }  
  121.         //处理网络写  
  122.         //每隔一段时间才查看检查是否有写事件需要处理  
  123.         if (currentTime.msec() - _write_time.msec() >= (unsigned long)(pool->usleep_time/1000))  
  124.         {  
  125.             _write_time = currentTime;  
  126.             if (!tasks.empty())  
  127.             {  
  128.                 //检查epoll描述符(kdpfd)有事件的连接(写事件一直存在)  
  129.                 int retcode = epoll_wait(kdpfd, &epfds[0], task_count, 0);//处理其他服务器的连接  
  130.                 if (retcode > 0)  
  131.                 {  
  132.                     for(int i = 0; i < retcode; i++)  
  133.                     {  
  134.                         tcp_session *task = (tcp_session *)epfds[i].data.ptr;  
  135.                         if (epfds[i].events & (EPOLLERR | EPOLLPRI))//错误则中断任务  
  136.                         {  
  137.                             //套接口出现错误  
  138.                             if(task->TerminateError())  
  139.                             {  
  140.                                 g_log->debug("%s: 套接口异常错误:%u", __PRETTY_FUNCTION__,epfds[i].events);  
  141.                                 task->Terminate(tcp_session::terminate_active);  
  142.                             }  
  143.                             else  
  144.                             {  
  145.                                 if (task->isFdsrAdd())  
  146.                                 {  
  147.                                     task->delEpoll(kdpfd_r, EPOLLIN | EPOLLERR | EPOLLPRI);  
  148.                                 }  
  149.                                 task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);  
  150.                             }  
  151.                         }  
  152.                         else  
  153.                         {  
  154.                             //再次是否有读事件(epoll描述符kdpfd)  
  155.                             这里又进行一次读事件检查,是为了尽量多处理事件,  
  156.                             //这样可以尽量减少系统调用从而提高效率(epoll使用的是水平触发,就算这里不处理读事件也是可以的,  
  157.                             //但是还是尽量多处理掉多些事件来减少epoll_wait的调用)  
  158.                             if (epfds[i].events & EPOLLIN)  
  159.                             {  
  160.                                 //连接的接收数据  
  161.                                 //套接口准备好了读取操作  
  162.                                 if (!task->ListeningRecv(true))  
  163.                                 {  
  164.                                     g_log->debug("%s: 套接口读操作错误,errno:%u, strerror:%s", __PRETTY_FUNCTION__,errno, strerror(errno));  
  165.                                     task->Terminate(tcp_session::terminate_active);  
  166.                                 }  
  167.                             }  
  168.                             //检查连接的写事件(连接的写事件一直存在,目的检查的是写缓存)  
  169.                             if (epfds[i].events & EPOLLOUT)//处理写事件  
  170.                             {  
  171.                                 //连接写数据  
  172.                                 //套接口准备好了写入操作  
  173.                                 if (!task->ListeningSend())  
  174.                                 {  
  175.                                     g_log->debug("%s: 套接口写操作错误errno:%u,strerrno:%s", __PRETTY_FUNCTION__,errno,strerror(errno));                                     task->Terminate(tcp_session::terminate_active);  
  176.                                 }  
  177.                             }  
  178.                         }  
  179.                         epfds[i].events=0;//该事件处理完后去掉该标识  
  180.                     }  
  181.                 }  
  182.             }  
  183.             check=true;  
  184.         }  
  185.     }  
  186.     //进程关闭前回收所有的会话  
  187.     //回收所有的会话:  
  188.     //线程关闭前就先要把所有的连接加入回收线程的队列,回收这些连接  
  189.     for(it = tasks.begin(); it != tasks.end();)  
  190.     {  
  191.         tcp_session *task = *it;  
  192.         remove(it);  
  193.         // state_okay -> state_recy  
  194.         /* 
  195.         * cjy 
  196.         * 先设置状态再添加容器, 
  197.         */  
  198.         task->getNextState();  
  199.         pool->addRecycle(task);  
  200.     }  
  201.     TEMP_FAILURE_RETRY(::close(kdpfd_r));//关闭epoll描述符  
  202. }





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值