• UDT源码剖析(六)之EPoll


    常用的事件处理体系EPoll在UDT中有一种独特的用法,初见时觉得鸡肋,越到后来越是觉得妙趣横生。

    EPoll

    • 基础数据结构:
    struct CEPollDesc
    {
       int m_iID;                                //    //在map中的索引ID
       std::set<UDTSOCKET> m_sUDTSocksOut;       // UDT关注的写事件
       std::set<UDTSOCKET> m_sUDTSocksIn;        // UDT关注的读事件
       std::set<UDTSOCKET> m_sUDTSocksEx;        // UDT关注的异常事件
    
       int m_iLocalID;                           //真实的由epoll_create返回的EID
       std::set<SYSSOCKET> m_sLocals;            // UDP/TCP之类的系统描述符
    
       std::set<UDTSOCKET> m_sUDTWrites;         // 可以提交给用户的UDT写事件
       std::set<UDTSOCKET> m_sUDTReads;          // 可以提交给用户的UDT读事件
       std::set<UDTSOCKET> m_sUDTExcepts;        // 可以提交给用户的UDT异常事件 (包括连接BROKEN之类的事件)
    };
    
    class CEPoll    //这个CEpoll使用map同时管理多个epoll实例,每个epoll实例管理1024个描述符
    {
    private:
       int m_iIDSeed;                            //缓存从map中索引epoll实例的ID,每次+1,到0x7fffffff后回绕,不过肯定没有这么多的epoll实例存在
       udt_pthread_mutex_t m_SeedLock;
    
       std::map<int, CEPollDesc> m_mPolls;       // 所有的EPoll
       udt_pthread_mutex_t m_EPollLock;
    };
    
    • 初始化:CEPoll::CEPoll()
    CEPoll::CEPoll():
    m_iIDSeed(0)    //将种子初始化为0
    {
       CGuard::createMutex(m_EPollLock);
    }
    
    • 创建真实的EPoll:int CEPoll::create()
    int CEPoll::create()
    {
       CGuard pg(m_EPollLock);
    
       int localid = 0;
    
       #ifdef LINUX
       localid = epoll_create(1024);    //每个创建一个只管理1024个描述符的EPoll
       if (localid < 0)
          throw CUDTException(-1, 0, errno);
       #else
       // on BSD, use kqueue
       // on Solaris, use /dev/poll
       // on Windows, select
       #endif
    
       if (++ m_iIDSeed >= 0x7FFFFFFF)    //防止种子进行回绕
          m_iIDSeed = 0;
    
       CEPollDesc desc;    //创建一个Desc初始化后加入EPoll管理的map中
       desc.m_iID = m_iIDSeed;
       desc.m_iLocalID = localid;
       m_mPolls[desc.m_iID] = desc;
    
       return desc.m_iID;
    }
    
    • 向EPoll中添加UDT SOCKET:int CEPoll::add_usock(const int eid, const UDTSOCKET& u, const int* events)
    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);      //找到合适的epoll
       if (p == m_mPolls.end())   
          throw CUDTException(5, 13);
    
       // BARCHART: Manage all event types.
       if (!events || (*events & UDT_EPOLL_IN))     //向Desc的观察描述符set中添加关注的事件
          p->second.m_sUDTSocksIn.insert(u);
       if (!events || (*events & UDT_EPOLL_OUT))
          p->second.m_sUDTSocksOut.insert(u);
       if (!events || (*events & UDT_EPOLL_ERR))
          p->second.m_sUDTSocksEx.insert(u);
    
       return 0;
    }
    
    • 更新EPoll中的UDT SOCKET关注的事件:int CEPoll::update_usock(const int eid, const UDTSOCKET& u, const int* events)
    int CEPoll::update_usock(const int eid, const UDTSOCKET& u, const int* events)
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);    //寻找合适的EPoll
       if (p == m_mPolls.end()){
            throw CUDTException(5, 13);
       }
    
       if(events){    //根据提供的事件,在Desc的观察set中添加或者删除事件
        if (*events & UDT_EPOLL_IN){    
            p->second.m_sUDTSocksIn.insert(u);
        }else{
            p->second.m_sUDTSocksIn.erase(u);
        }
        if (*events & UDT_EPOLL_OUT){
            p->second.m_sUDTSocksOut.insert(u);
        } else{
            p->second.m_sUDTSocksOut.erase(u);
        }
       }
    
       return 0;
    }
    
    • 获取UDT SOCKET关注的事件:int CEPoll::verify_usock(const int eid, const UDTSOCKET& u, int* events)
    int CEPoll::verify_usock(const int eid, const UDTSOCKET& u, int* events)
    {
    
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);    //寻找合适的EPoll
       if (p == m_mPolls.end()){
            throw CUDTException(5, 13);
       }
    
       if(events){
        if(p->second.m_sUDTSocksIn.find(u) != p->second.m_sUDTSocksIn.end()){    //将关注的事件填充到event中
            *events |= UDT_EPOLL_IN;
        }
        if(p->second.m_sUDTSocksOut.find(u) != p->second.m_sUDTSocksOut.end()){
            *events |= UDT_EPOLL_OUT;
        }
       }
    
       return 0;
    
    }
    
    • 向EPoll中添加SYS SOCKET,使用epoll系列函数:int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
    int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);    //寻找合适的EPoll
       if (p == m_mPolls.end())
          throw CUDTException(5, 13);
    
    #ifdef LINUX
       epoll_event ev;    //获取事件
    
       if (NULL == events)
          ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;    //如果没有提供关注的事件,则SYS默认关注所有的事件
       else
       {
          ev.events = 0;    //否则根据提供的事件列表决定关注哪些事件
          if (*events & UDT_EPOLL_IN)
             ev.events |= EPOLLIN;
          if (*events & UDT_EPOLL_OUT)
             ev.events |= EPOLLOUT;
          if (*events & UDT_EPOLL_ERR)
             ev.events |= EPOLLERR;
       }
    
       ev.data.fd = s;
       if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0)
          throw CUDTException();
    #endif
    
       p->second.m_sLocals.insert(s);    //向描述的SYS SOCKET中添加事件
    
       return 0;
    }
    
    • 删除EPoll中关注的UDT SOCKET:int CEPoll::remove_usock(const int eid, const UDTSOCKET& u)
    int CEPoll::remove_usock(const int eid, const UDTSOCKET& u)
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);    //寻找EPoll Desc
       if (p == m_mPolls.end())
          throw CUDTException(5, 13);
    
       p->second.m_sUDTSocksIn.erase(u);    //从关注读写的set中删除UDT SOCKET
       p->second.m_sUDTSocksOut.erase(u);
    
       p->second.m_sUDTReads.erase(u);    //从读写已发送的set中删除UDT SOCKET
       p->second.m_sUDTWrites.erase(u);
    
       return 0;
    }
    
    • 删除EPoll关注的SYS SOCKET,使用epoll系列函数:int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
    int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);    //获取EPoll Desc
       if (p == m_mPolls.end())
          throw CUDTException(5, 13);
    
    #ifdef LINUX
       epoll_event ev;     //删除关注的SYS SOCKET
       if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0)
          throw CUDTException();
    #endif
    
       p->second.m_sLocals.erase(s);
    
       return 0;
    }
    
    int CEPoll::wait(const int eid, set<UDTSOCKET>* readfds, set<UDTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
    {
       if (!readfds && !writefds && !lrfds && lwfds && (msTimeOut < 0))    // 如果提供的参数有问题,就抛出异常
          throw CUDTException(5, 3, 0);
    
       //因为是填充,先清除提供的参数
       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);    //找到相应的epoll实例,没找到就抛异常,在抛出异常前先释放锁
          if (p == m_mPolls.end())
          {
             CGuard::leaveCS(m_EPollLock);
             throw CUDTException(5, 13);
          }
    
          //如果找到的epoll观察的各种参数都是空的,抛出异常
          if (p->second.m_sUDTSocksIn.empty() && p->second.m_sUDTSocksOut.empty() && p->second.m_sLocals.empty() && (msTimeOut < 0))
          {
             CGuard::leaveCS(m_EPollLock);
             throw CUDTException(5, 3);
          }
    
          // 将已发生的读事件和异常事件(UDTSOCKET)填充到用户提供的容器中,读事件是直接获得描述符set,异常事件是加入,然后更新total.写事件做同样的处理
          if ((NULL != readfds) && (!p->second.m_sUDTReads.empty() || !p->second.m_sUDTExcepts.empty()))
          {
             *readfds = p->second.m_sUDTReads;    //不进行复制,直接将用户提供的指针指向UDT的可读set,然后将异常UDT添加到可读set中
             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()))
          {
             *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();
          }
    
          // 因为异常事件的描述符已经添加,清除当前epoll实例中的异常事件
          if(total > 0 && !p->second.m_sUDTExcepts.empty()){
            p->second.m_sUDTExcepts.clear();
          }
    
          //处理系统事件  
          if (lrfds || lwfds)
          {
             #ifdef LINUX
             const int max_events = p->second.m_sLocals.size();       //获取EPoll Desc中系统描述符数量,并对系统描述符做epoll_wait处理
             epoll_event ev[max_events];
             int nfds = ::epoll_wait(p->second.m_iLocalID, ev, max_events, 0);    //调用wait函数处理这些事件
            
             //填充系统描述符的返回列表,累加total
             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 readfds2;
             fd_set writefds2;
             FD_ZERO(&readfds2);
             FD_ZERO(&writefds2);
    
             for (set<SYSSOCKET>::const_iterator i = p->second.m_sLocals.begin(); i != p->second.m_sLocals.end(); ++ i)
             {
                if (lrfds)
                   FD_SET(*i, &readfds2);
                if (lwfds)
                   FD_SET(*i, &writefds2);
             }
    
             timeval tv;
             tv.tv_sec = 0;
             tv.tv_usec = 0;
             if (::select(0, &readfds2, &writefds2, 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, &readfds2))
                   {
                      lrfds->insert(*i);
                      ++ total;
                   }
                   if (lwfds && FD_ISSET(*i, &writefds2))
                   {
                      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;
    }
    
    • 释放某一个EPoll:int CEPoll::release(const int eid)
    int CEPoll::release(const int eid)    
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator i = m_mPolls.find(eid);    //获取EPoll Desc
       if (i == m_mPolls.end())
          throw CUDTException(5, 13);
    
       #ifdef LINUX
       // release local/system epoll descriptor
       ::close(i->second.m_iLocalID);    //直接关闭eid
       #endif
    
       m_mPolls.erase(i);    //从map中移除Epoll Desc
    
       return 0;
    }
    
    • 更新事件:int CEPoll::update_events(const UDTSOCKET& uid, std::set<int>& eids, int events, bool enable)
    int CEPoll::update_events(const UDTSOCKET& uid, std::set<int>& eids, int events, bool enable)
    {
       CGuard pg(m_EPollLock);
    
       map<int, CEPollDesc>::iterator p;    //获取EPoll Desc
    
       vector<int> lost;
       for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)    //在所有的EPoll Desc中进行轮询
       {   
          p = m_mPolls.find(*i);    //获取一个EPoll Dssc
          if (p == m_mPolls.end())  //如果没有找到这个epoll,就把这个ID加入lost中
          {
             lost.push_back(*i);
          }
          else
          {    //找到的话,根据参数决定是否将事件从watch中添加到result中
             if ((events & UDT_EPOLL_IN) != 0)
                update_epoll_sets(uid, p->second.m_sUDTSocksIn, p->second.m_sUDTReads, enable);
             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);
          }
       }
        
       //如果没有找到合适的EPoll,移除这些丢失的EPoll
       for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
          eids.erase(*i);
    
       return 0;
    }
    
    • 独立于EPoll的函数:void update_epoll_sets(const UDTSOCKET& uid, const set<UDTSOCKET>& watch, set<UDTSOCKET>& result, bool enable)
    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);
       }
    }
    
  • 相关阅读:
    记录vue
    vue记录
    布局例子
    element ui + vue.js + asp.net mvc 实现单页面程序——使用 ASP.NET Core MVC 生成 Web API 和 Web UI——(20220818)
    标准index.html模板
    开发记录
    HelloWorld.vue
    Vue+ElementUI+.netcore前后端分离框架开发项目——项目开发记录20220816——使用 ASP.NET Core MVC 生成 Web API 和 Web UI
    script笔记1
    基于elementui 动态换肤的代码详解
  • 原文地址:https://www.cnblogs.com/ukernel/p/9191055.html
Copyright © 2020-2023  润新知