日期:2014-05-16  浏览次数:20663 次

一个关于EPOLL的问题,一直没有办法进行解决!请大侠们来帮帮忙啊!
我编写的epoll的时候发现一个很怪异的问题。
我在windows写编写的客户端程序来链接LINUX下用epoll写的服务端程序。
当进行少量的客户端链接时,客户端发送数据可以被正确的接收到。
当进行大量的客户端链接时(300个客户端),客户端发送的数据在epoll接收的时候就会很慢,而且有的时候无法接收完全。
我的实现是这样的。
主进程创建一个wait线程,用来专门等待EPOLL的事件,代码如下:
fds = epoll_wait(lpThreadObj->Parent->m_epollEvents->epoll_fd, lpThreadObj->Parent->m_epollEvents->epoll_events, lpThreadObj->Parent->m_epollEvents->maxfds, 0);
  if (fds <= 0)
  {
  if (STATE_TERMINATED == lpThreadObj->m_state)
  {
  sem_post(&lpThreadObj->Wait_exit_sem);
  return NULL;
  }
  continue;
  }
  else
  {
  for(int i = 0; i < fds; i++)
  {
  CSocketClient* pClient = (struct CSocketClient*)lpThreadObj->Parent->m_epollEvents->epoll_events[i].data.ptr;
  if(pClient == NULL) continue;

  if((pClient->Socket == lpThreadObj->Parent->m_ListenClient->Socket) && (pClient->Socket & EPOLLIN))
  {
  sem_post(&lpThreadObj->Accept_Work_sem);
  continue;
  }

  if(lpThreadObj->Parent->m_epollEvents->epoll_events[i].events & EPOLLIN)
  {
  gettimeofday(&pClient->m_ActiveTick, NULL);

  //加入接收队列中进行等待处理
  lpThreadObj->addRecvQueue(pClient, OPER_RECEIVE);
  }
  else if(lpThreadObj->Parent->m_epollEvents->epoll_events[i].events & EPOLLOUT)
  {
  //printf("EPOLLOUT = %d i = %d\n", pClient->SocketHandle, i);
  }
  }
  }


sem_post(&lpThreadObj->Accept_Work_sem);会触发一个事件,另外会有一个CEpollAcceptWorkThread线程专门来进行accept
并对每个客户端的链接对象添加监控事件EPOLLIN|EPOLLOUT|EPOLLET。代码如下:
struct epoll_event event;
  event.data.fd = pSocket->Socket;
  event.data.ptr = pSocket;
  event.events = EPOLLIN|EPOLLOUT|EPOLLET;
  int iRet = epoll_ctl(m_epollEvents->epoll_fd, EPOLL_CTL_ADD, pSocket->Socket, &event);
lpThreadObj->addRecvQueue(pClient, OPER_RECEIVE);函数是将读取请求加入到一个待处理队列中。有3个读取线程从此队列获得读取请求,并进行读取。

少量的数据的时候一切都是正常的,当300个链接以上接收数据就和客户端发送的数据量不一致了。

我的环境是:
EPOLL在UBUNTU 11.10系统下开发,UBUNTU 安装在windows下的虚拟机中。
客户端在windows下 




------解决方案--------------------
肯定是编码问题,注意检查这几个:

1,write和read的返回值是否检查了,因为返回值可能小于请求值或者直接返回-1。
2,你用的ET,编码时是否read/write到发生EAGAIN为止,不读干净就会漏掉数据。、
3,不应该始终注册EPOLLOUT,因为只要网卡不满,永远都会触发EPOLLOUT事件,也就是即便没有任何消息,你的程序都会不停的循环。
------解决方案--------------------
你讲讲你的服务端架构吧, 就这点代码来看的话是主线程将触发事件的CLIENT放入队列,利用信号量唤醒工作线程,光这么点东西要注意的细节就很多。 

一个CLIENT会不会被多次放入队列中? 如果你没有考虑这个情况, 那么你就肯定没有考虑到同一个CLIENT被不同的线程同时处理的情况。

这种靠线程的架构, 要不就是事件I/O线程读数据拆包,然后交给线程池去处理,之后再通知事件I/O线程发回数据,一个client只能被某一个线程负责,不能一会给这个线程一会给那个线程,这样包序是一个大麻烦,并发也没法处理,你应该理解。

依靠线程的架构说白了就一种,具体一点就是两种。
一种是每个线程负责若干socket的事件监听,这适合CPU多的,非计算的网络通信,主线程只负责accept。
一种是每个线程负责一个socket的处理,适合计算密集的网络通信,主线程还是只负责accept。

你这种写法我看命名就觉得有问题,因为你的主线程肯定不是从accept之后就撒手不管了,而是主线程监听,不停地将client分发到线程,这是不好的。

应该accept之后就将client分发到某个线程,从此改client的死活都由那个线程负责了。
你的问题就是每次client有事件都分发一次,请问如果数据密集了,同一个client在短期内多次来数据,被分发到不同的线程并发处理,请问怎么read怎么send? 毫无办法。
------解决方案------------