epoll 是为处理大量句柄而改进的poll,在UDT中也有支持。UDT使用了内核提供的epoll,主要是epoll_create,epoll_wait,epoll_ctl,UDT定义了CEPollDesc这个结构来管理epoll的描述符和套接字。
struct CEPollDesc
{
   int m_iID;                                // epoll ID
   std::set<UDTSOCKET> m_sUDTSocksOut;       // set of UDT sockets waiting for write events
   std::set<UDTSOCKET> m_sUDTSocksIn;        // set of UDT sockets waiting for read events
   std::set<UDTSOCKET> m_sUDTSocksEx;        // set of UDT sockets waiting for exceptions
 
   int m_iLocalID;                           // local system epoll ID
   std::set<SYSSOCKET> m_sLocals;            // set of local (non-UDT) descriptors
 
   std::set<UDTSOCKET> m_sUDTWrites;         // UDT sockets ready for write
   std::set<UDTSOCKET> m_sUDTReads;          // UDT sockets ready for read
   std::set<UDTSOCKET> m_sUDTExcepts;        // UDT sockets with exceptions (connection broken, etc.)
};
特别要提醒的是,当对端socket连接中断的时候,也是在m_sUDTReads里的
UDT还实现了一个类来进行各项操作,实现的有
create():创建一个epoll,调用了epoll_create
add_usock():添加一个UDT套接字到epoll
add_ssock():添加一个系统套接字到epoll,调用了epoll_ctl
remove_usock():从epoll中移除一个UDT套接字
remove_ssock():从epoll中移除一个系统套接字,调用了epoll_ctl
wait():等待epoll事件或者超时,调用了epoll_wait
release():关闭并释放一个epoll
 
UDT里对epoll的调用是四段式的,比如add_usock这里,调用顺序是epoll_add_usock()->CUDT::epoll_add_usock()->s_UDTUnited.epoll_add_usock()->CEPoll::add_usock
 
int epoll_add_usock(int eid, UDTSOCKET u, const int* events)
{
   return CUDT::epoll_add_usock(eid, u, events);
}
 
int CUDT::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events)
{
   try
   {
      return s_UDTUnited.epoll_add_usock(eid, u, events);
   }
   catch (CUDTException e)
   {
      s_UDTUnited.setError(new CUDTException(e));
      return ERROR;
   }
   catch (...)
   {
      s_UDTUnited.setError(new CUDTException(-1, 0, 0));
      return ERROR;
   }
}
 
int CUDTUnited::epoll_add_usock(const int eid, const UDTSOCKET u, const int* events)
{
   CUDTSocket* s = locate(u);
   int ret = -1;
   if (NULL != s)
   {
      ret = m_EPoll.add_usock(eid, u, events);
      s->m_pUDT->addEPoll(eid);
   }
   else
   {
      throw CUDTException(5, 4);
   }
 
   return ret;
}
 
int CEPoll::add_usock(const int eid, const UDTSOCKET& u, const int* events)
{
   CGuard pg(m_EPollLock);
 
   map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
   if (p == m_mPolls.end())
      throw CUDTException(5, 13);
 
   if (!events || (*events & UDT_EPOLL_IN))       //UDT_EPOLL_IN 和UDT_EPOLL_OUT、UDT_EPOLL_ERROR分别是0x1, 0x4, 0x8,可以进行&运算
      p->second.m_sUDTSocksIn.insert(u);
   if (!events || (*events & UDT_EPOLL_OUT))
      p->second.m_sUDTSocksOut.insert(u);
 
   return 0;
}
 
UDT命名空间提供给应用程序调用接口,可成为API层,API层调用CUDT API,这一层主要做错误处理,捕捉下面两层抛出的错误保存起来交给应用程序使用,CUDT API调用CUDTUnited的实现,如果是UDT SOCKET的socket(),bind(),listen()等,到这层也就结束了,不过epoll要多个第四层,再调用CEPoll的实现。现在来看看CUDTUnited和CEPoll的实现。
CUDTSocket* s = locate(u);
 调用CUDTUnited::locate(),根据SocketID,也就是UDT Socket handle在CUDTUnited的std::map<UDTSOCKET, CUDTSocket*> m_Sockets中找到对应的CUDTSocket结构,如果找不到,抛出错误,找到了就调用CEPoll的add_usock实现,根据传的event参数,将socket导入相应的队列。之后调用s->m_pUDT->addEPoll(eid)
void CUDT::addEPoll(const int eid)
{
   CGuard::enterCS(s_UDTUnited.m_EPoll.m_EPollLock);                      //这种通过类来实现加锁解锁的,我第一次见,还挺方便。
   m_sPollID.insert(eid);
   CGuard::leaveCS(s_UDTUnited.m_EPoll.m_EPollLock);
 
   if (!m_bConnected || m_bBroken || m_bClosing)
      return;
 
   if (((UDT_STREAM == m_iSockType) && (m_pRcvBuffer->getRcvDataSize() > 0)) ||
      ((UDT_DGRAM == m_iSockType) && (m_pRcvBuffer->getRcvMsgNum() > 0)))
   {
      s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_IN, true);
   }
   if (m_iSndBufSize > m_pSndBuffer->getCurrBufSize())
   {
      s_UDTUnited.m_EPoll.update_events(m_SocketID, m_sPollID, UDT_EPOLL_OUT, true);
   }
}
这里已经开始更新m_sUDTWrites,m_sUDTReads,通过update_events(),如前所述,update_events()也是CEPoll的成员函数。
 
int CEPoll::update_events(const UDTSOCKET& uid, std::set<int>& eids, int events, bool enable)
{
   CGuard pg(m_EPollLock);
 
   map<int, CEPollDesc>::iterator p;
 
   vector<int> lost;
   for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)
   {
      p = m_mPolls.find(*i);
      if (p == m_mPolls.end())
      {
         lost.push_back(*i);
      }
      else
      {
         if ((events & UDT_EPOLL_IN) != 0)
            update_epoll_sets(uid, p->second.m_sUDTSocksIn, p->second.m_sUDTReads, enable);   //UDT_EPOLL_IN 和UDT_EPOLL_OUT、UDT_EPOLL_ERROR分别是0x1, 0x4, 0x8,可以进行&运算
         if ((events & UDT_EPOLL_OUT) != 0)
            update_epoll_sets(uid, p->second.m_sUDTSocksOut, p->second.m_sUDTWrites, enable);
         if ((events & UDT_EPOLL_ERR) != 0)
            update_epoll_sets(uid, p->second.m_sUDTSocksEx, p->second.m_sUDTExcepts, enable);
      }
   }
 
   for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
      eids.erase(*i);
 
   return 0;
}
 
void update_epoll_sets(const UDTSOCKET& uid, const set<UDTSOCKET>& watch, set<UDTSOCKET>& result, bool enable)
{
   if (enable && (watch.find(uid) != watch.end()))
   {
      result.insert(uid);
   }
   else if (!enable)
   {
      result.erase(uid);
   }
}
 
最后说下wait函数的实现,一样是四段式,就跳过前面三段,直接看第四段
 
int CEPoll::wait(const int eid, set<UDTSOCKET>* readfds, set<UDTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
{
   // if all fields is NULL and waiting time is infinite, then this would be a deadlock   都空的的话,会死锁,抛出异常
   if (!readfds && !writefds && !lrfds && lwfds && (msTimeOut < 0))
      throw CUDTException(5, 3, 0);
 
   // Clear these sets in case the app forget to do it.  清空
   if (readfds) readfds->clear();
   if (writefds) writefds->clear();
   if (lrfds) lrfds->clear();
   if (lwfds) lwfds->clear();
 
   int total = 0;
 
   int64_t entertime = CTimer::getTime();
   while (true)
   {
      CGuard::enterCS(m_EPollLock);
 
      map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
      if (p == m_mPolls.end())
      {
         CGuard::leaveCS(m_EPollLock);
         throw CUDTException(5, 13);
      }
 
      if (p->second.m_sUDTSocksIn.empty() && p->second.m_sUDTSocksOut.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0))
      {
         // no socket is being monitored, this may be a deadlock  都空的的话,会死锁,抛出异常
         CGuard::leaveCS(m_EPollLock);
         throw CUDTException(5, 3);
      }
 
      // Sockets with exceptions are returned to both read and write sets.          把m_sUDTReads和m_sUDTExcepts都读到readfds里
      if ((NULL != readfds) && (!p->second.m_sUDTReads.empty() || !p->second.m_sUDTExcepts.empty()))
      {
         *readfds = p->second.m_sUDTReads;
         for (set<UDTSOCKET>::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i)
            readfds->insert(*i);
         total += p->second.m_sUDTReads.size() + p->second.m_sUDTExcepts.size();
      }
      if ((NULL != writefds) && (!p->second.m_sUDTWrites.empty() || !p->second.m_sUDTExcepts.empty()))          //把m_sUDTWrites和m_sUDTExcepts都读到writefds里
      {
         *writefds = p->second.m_sUDTWrites;
         for (set<UDTSOCKET>::const_iterator i = p->second.m_sUDTExcepts.begin(); i != p->second.m_sUDTExcepts.end(); ++ i)
            writefds->insert(*i);
         total += p->second.m_sUDTWrites.size() + p->second.m_sUDTExcepts.size();
      }
 
      if (lrfds || lwfds)     //读系统套接字
      {
         #ifdef LINUX
         const int max_events = p->second.m_sLocals.size();
         epoll_event ev[max_events];
         int nfds = ::epoll_wait(p->second.m_iLocalID, ev, max_events, 0);
 
         for (int i = 0; i < nfds; ++ i)
         {
            if ((NULL != lrfds) && (ev[i].events & EPOLLIN))
           {
               lrfds->insert(ev[i].data.fd);
               ++ total;
            }
            if ((NULL != lwfds) && (ev[i].events & EPOLLOUT))
            {
               lwfds->insert(ev[i].data.fd);
               ++ total;
            }
         }
         #else
         //currently "select" is used for all non-Linux platforms.
         //faster approaches can be applied for specific systems in the future.
 
         //"select" has a limitation on the number of sockets
 
         fd_set readfds;
         fd_set writefds;
         FD_ZERO(&readfds);
         FD_ZERO(&writefds);
 
         for (set<SYSSOCKET>::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i)
         {
            if (lrfds)
               FD_SET(*i, &readfds);
            if (lwfds)
               FD_SET(*i, &writefds);
         }
 
         timeval tv;
         tv.tv_sec = 0;
         tv.tv_usec = 0;
         if (::select(0, &readfds, &writefds, NULL, &tv) > 0)
         {
            for (set<SYSSOCKET>::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i)
            {
               if (lrfds && FD_ISSET(*i, &readfds))
               {
                  lrfds->insert(*i);
                  ++ total;
               }
               if (lwfds && FD_ISSET(*i, &writefds))
               {
                  lwfds->insert(*i);
                  ++ total;
               }
            }
         }
         #endif
      }
 
      CGuard::leaveCS(m_EPollLock);
 
      if (total > 0)
         return total;
 
      if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * 1000LL))
         throw CUDTException(6, 3, 0);
 
      CTimer::waitForEvent();
   }
 
   return 0;
}

UDT中的epoll的更多相关文章

  1. netty系列之:真正的平等–UDT中的Rendezvous

    目录 简介 建立支持Rendezvous的服务器 处理不同的消息 节点之间的交互 总结 简介 在我们之前提到的所有netty知识中,netty好像都被分为客户端和服务器端两部分.服务器端监听连接,并对 ...

  2. UDT中epoll对CLOSE状态的处理

    epoll_wait()返回可用uid时,对uid取状态,本该是BROKEN的,却取到CLOSED,然而,不能像处理BROKEN事件那样处理CLOSED事件,这样移除不了CLOSED事件,于是epol ...

  3. 解决UDT中内存下不去的问题

         使用UDT库,编写简单的网络通信程序,发现了一个问题,关闭一部分连接后,程序占用内存并没有变化.      比如先连接500个,再连接另500个,先关掉后面500个,程序占用内存降一半,再关 ...

  4. nginx中的epoll模型

    要了解epoll模型,就要一个一个知识点由浅至深地去探索. 1.IO复用技术 IO流请求操作系统内核,有串行处理和并行处理两种概念. 串行处理是前面一个操作处理地时候,后面的所有操作都需要等待.因此, ...

  5. 五种网络IO模型以及多路复用IO中select/epoll对比

    下面都是以网络读数据为例 [2阶段网络IO] 第一阶段:等待数据 wait for data 第二阶段:从内核复制数据到用户 copy data from kernel to user 下面是5种网络 ...

  6. 服务器 libevent中epoll使用实例demo

    名词解释:man epoll之后,得到如下结果: NAME       epoll - I/O event notification facility SYNOPSIS       #include ...

  7. 应用服务器中对JDK的epoll空转bug的处理

    原文链接:应用服务器中对JDK的epoll空转bug的处理 前面讲到了epoll的一些机制,与select和poll等传统古老的IO多路复用机制的一些区别,这些区别实质可以总结为一句话, 就是epol ...

  8. Python——在Python中如何使用Linux的epoll

    在Python中如何使用Linux的epoll 目录 序言 阻塞socket编程示例 异步socket的好处以及Linux epoll 带epoll的异步socket编程示例 性能注意事项 源代码 序 ...

  9. 快来体验快速通道,netty中epoll传输协议详解

    目录 简介 epoll的详细使用 EpollEventLoopGroup EpollEventLoop EpollServerSocketChannel EpollSocketChannel 总结 简 ...

随机推荐

  1. 泛函编程(29)-泛函实用结构:Trampoline-不再怕StackOverflow

    泛函编程方式其中一个特点就是普遍地使用递归算法,而且有些地方还无法避免使用递归算法.比如说flatMap就是一种推进式的递归算法,没了它就无法使用for-comprehension,那么泛函编程也就无 ...

  2. [moka同学笔记]Yii2 自定义class、自定义全局函数(摘录)

    1.在app\components下新建MyComponent.PHP namespace app\components; use Yii; use yii\base\Component; use y ...

  3. 参数化命令相关知识点之==================防止SQl的注入

    一: 使用参数化命令查询DAL类: public DataTable StudentDAL(string name,string gender) { string str="连接字符串&qu ...

  4. rabbitmq qos prefetch count的设置与作用

    因为原来使用了MQ作为rpc机制,随着客户交易量越来越大,很多服务器推送行情的压力很大,最近打算重写为批量模式,又重新看了下qos和prefetch设置的作用以确定优化的具体细节. 消费者在开启ack ...

  5. jquery.datepair日期时分秒选择器

    jquery.datepair是一个轻量级的jQuery插件,智能选择日期和时间范围,灵感来自于谷歌日历.Datepair将保持开始和结束日期/时间同步,并可以根据用户的操作设置默认值.该插件不提供任 ...

  6. Type.js – 帮助你更好的控制网页字体排版

    Type.js 是一款很好的网页字体排版工具.它可以让你使用新的 CSS 属性,在网页上试下更精细的排版控制.设置很简单.上传 type.js 到您的网站,并在你的 HTML 链接中引用.接下来,你就 ...

  7. easyui1.3.2中使用1.3.6或1.4.x的calendar

    首先在1.3.2中calendar控件不支持日历某天的颜色进行改变,和自定义回调函数 Name Type Description Default width number The width of c ...

  8. Winform 图片鼠标滚动查看(放大,缩小,旋转,拖动查看)[日常随笔]

    方法千千万,我只是其中一笔[通过控制PictureBox来控制图片,图片完全施展在控件中]...几久不做,还真有点陌生! 窗体构造中添加鼠标滚动: /// <summary> /// 窗体 ...

  9. Weinre调试移动端页面

    Weinre是什么 如果我们做的是Cordova(phonegap)或其他hybird应用,当使用到原生功能时候(类似原生请求数据或页面切换时),没办法在PC chrome浏览器调试页面,一旦页面在手 ...

  10. JavaScript学习笔记-setTimeout应用

    setTimeout应用 var ids = [];function foo1(i) { this.i = i; console.log('i = '+i); ids[0] = setTimeout( ...