业务网络收发线程的循环部分,处理网络数据的接收和发送。
设计上:
(1)先检查所有连接是否处于正常状态。
(2)网络的处理是读优先的,有读才检查写,写的检查是一段时间检查一次(目前是50ms)。
实现上通过两个epoll描述符来处理。
第一个epoll描述符监听所有连接的读事件,并处理网络数据的接收。
第二个epoll描述符监听所有连接的写事件(顺带会检查读事件),并处理网络数据的发送(顺带会处理有读事件的连接的接收)。
处理步骤:
(1)例行检查: 遍历所有连接,检查连接是否有读事件,发送测试信号(心跳)到客户端,断开需要断开的连接并回收
(2)检查需要检查的连接的读事件,并处理epoll读事件(非阻塞检查)
(3)每隔一段时间并且在有读事件情况下的检查是否有写缓存,若有数据则处理写事件(非阻塞检查)
- void main_service_thread::run()
- {
- realtime currentTime;
- realtime _write_time;
- tcp_session_IT it;
- int kdpfd_r;
- vector_epoll epfds_r;
- 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())
- {
- //遍历所有连接,检查连接是否有读事件,发送测试信号(心跳)到客户端,断开需要断开的连接并回收
- for(it = tasks.begin(); it != tasks.end();)//遍历所有连接
- {
- tcp_session *task = *it;
- //检查测试信号指令(检查是否需要发送网络测试信号(只有简单包头的没有内容的消息)到客户端)
- task->checkSignal(currentTime);
- //回收需要断开的连接
- 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
- {
- //添加需要读的连接到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);
- //检查需要检查的连接的读事件,并处理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);//这里可能导致fds_count_r大于需要监听读的连接数
- }
- task->delEpoll(kdpfd, EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLPRI);
- }
- }
- else
- {
- //接收有读事件的连接的数据
- 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;
- }
- }
- }
- //成功接收到数据才检查写缓存 (一直监听连接的写事件)
- //套接字出错或者有读事件但还没到读缓冲区则继续检查读,否则开始检查写缓存
- if(check)
- {
- continue;
- }
- //处理网络写
- //每隔一段时间才查看检查是否有写事件需要处理
- if (currentTime.msec() - _write_time.msec() >= (unsigned long)(pool->usleep_time/1000))
- {
- _write_time = currentTime;
- if (!tasks.empty())
- {
- //检查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
- {
- //再次是否有读事件(epoll描述符kdpfd)
- 这里又进行一次读事件检查,是为了尽量多处理事件,
- //这样可以尽量减少系统调用从而提高效率(epoll使用的是水平触发,就算这里不处理读事件也是可以的,
- //但是还是尽量多处理掉多些事件来减少epoll_wait的调用)
- if (epfds[i].events & EPOLLIN)
- {
- //连接的接收数据
- //套接口准备好了读取操作
- if (!task->ListeningRecv(true))
- {
- g_log->debug("%s: 套接口读操作错误,errno:%u, strerror:%s", __PRETTY_FUNCTION__,errno, strerror(errno));
- task->Terminate(tcp_session::terminate_active);
- }
- }
- //检查连接的写事件(连接的写事件一直存在,目的检查的是写缓存)
- if (epfds[i].events & EPOLLOUT)//处理写事件
- {
- //连接写数据
- //套接口准备好了写入操作
- 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;
- }
- }
- //进程关闭前回收所有的会话
- //回收所有的会话:
- //线程关闭前就先要把所有的连接加入回收线程的队列,回收这些连接
- for(it = tasks.begin(); it != tasks.end();)
- {
- tcp_session *task = *it;
- remove(it);
- // state_okay -> state_recy
- /*
- * cjy
- * 先设置状态再添加容器,
- */
- task->getNextState();
- pool->addRecycle(task);
- }
- TEMP_FAILURE_RETRY(::close(kdpfd_r));//关闭epoll描述符
- }