【问题标题】:How to filter epoll events?如何过滤 epoll 事件?
【发布时间】:2014-02-16 22:28:48
【问题描述】:

假设我在多个网络端口上监听: 80 带 TCP 81-250 使用 UDP

这需要多种不同的方式来处理输入。
- 端口 80 上的新套接字(将新文件描述符添加到 epoll 列表)
- 从端口 80 的客户端套接字读取
- 从其他 170 个 UDP 端口中的任何一个读取

这是很多不同的句柄,而我想使用 epoll 只对正在更新的句柄做出反应。
有什么办法可以过滤 epoll 事件,让每个事件都得到它需要的句柄,而不需要遍历所有文件描述符?

提前致谢,
有罪

示例代码:

int epfd = epoll_create( 10 );

int tcpFd = createTCPSocket( 80 );
registerListenEvent( epfd, tcpFd );

int udpFds[ 170 ];
for ( int i = 0; i < 170; i++ ) {
  udpSockets[ i ] = createUdpSocket( 81 + i );
  registerUdpPacketEvent( epfd, udpFds[ i ] );
}

int tcpClientFds[ 256 ];
int tcpClientId = 0;

struct epoll_event events[ 64 ];

while ( 1 ) {
  int nfds = epoll_wait( epfd, events, 64, -1 );
  for ( int i = 0; i < nfds; i++ ) {
    struct epoll_event event = events[ i ];
    int fd = event.data.fd;

    // This kind of filtering can take quite a while
    if ( fd == tcpFd ) {
      int acceptedFd = accept( tcpFd );
      registerTcpClientEvent( epfd, acceptedFd );
      tcpClientFds[ tcpClientId++ ] = acceptedFd;
    } else {
      for ( int i = 0; i < 170; i++ )
        if ( udpFds[ i ] == fd ) {
          handleUdpMessage( fd );
          return;
        }
     for ( int i = 0; i < tcpClientId; i++ ) {
       if ( tcpClientFds[ i ] == fd ) {
         handleTcpMessage( fd );
         return;
       }
     }
    }
  }
}

您可以想象,为每个事件执行 170 到 426 个循环可能会非常昂贵。 我希望可以在没有这个的情况下确定该事件。 这可能吗?

【问题讨论】:

  • 正在更新是什么意思?您想从描述符的子集中选择事件吗?

标签: c++ c linux sockets epoll


【解决方案1】:

epoll() 告诉您触发事件的确切文件描述符。您不必寻找它们。以Read the documentation 为例。 epoll_wait() 为您提供epoll_event 结构数组,每个满足的文件描述符都有一个。 epoll_event 结构有一个 fd 成员。您将为 EPOLLIN 事件注册您的个人套接字,然后每次 epoll_wait() 报告满意的事件时,您将根据需要仅从报告的套接字中读取。

【讨论】:

  • 这样我仍然必须确定那些文件描述符是什么。我想轻松找出它们是 TCP 侦听、TCP 读取还是 UDP。我在帖子中添加了一些示例代码以使其更清晰。
  • @Dennis events 成员告诉您该 FD 上发生了哪些事件。你有什么问题?
  • TCP fd 需要用read 读取,而UDP fd 需要用recvfrom 读取。无法根据 epoll 结果判断是 TCP 还是 UDP 套接字。或者有吗?
  • 或者您可以在使用epoll_ctl() 注册套接字时包含您自己的用户定义数据。 epoll_wait() 将报告相同的数据。因此,您可以使用自己的自定义标识符来了解哪些套接字是 TCP 与 UDP。
  • recv()recvfrom() 之间的唯一区别是recv() 不报告发件人IP:端口。在 TCP 中你不需要它(你可以使用 getpeername() 代替),但你几乎总是在 UDP 中需要它。因此,在大多数情况下,您不需要 TCP 中的 recvfrom() 或 UDP 中的 recv()
【解决方案2】:

就像提到的“remy”一样,为每个套接字创建自己的结构并将其添加到事件中。当事件首先触发时,请参考您的结构并开始处理。

    struct epoll_event event = {0, {NULL, 0, 0, 0}};
    int s = 0;

    event.data.fd = tp->task_socket;
    event.data->ptr = tp;
    event.events = EPOLLOUT;

    assert(efd);

    s = epoll_ctl (efd, EPOLL_CTL_DEL, tp->task_socket, &event);

处理部分:

案例 EPOLLOUT: tp = (task *)ev->data->ptr;

这个 tp 应该告诉你所有关于你的套接字和处理函数的信息..

【讨论】:

    猜你喜欢
    • 2016-01-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-08
    • 1970-01-01
    • 1970-01-01
    • 2019-09-24
    • 2011-02-27
    • 1970-01-01
    相关资源
    最近更新 更多