16

在侦听套接字上,我设置了EPOLLIN位,但是在客户端连接上,我将EPOLLIN | EPOLLOUT位设置为struct epoll_event

struct epoll_event ev;

ev.data.fd = fd;
ev.events = EPOLLIN | EPOLLOUT;
if (epoll_ctl(evs->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
     ...

这就是我测试位的方式:

if ((events & EPOLLIN) == EPOLLIN)
     ...
if ((events & EPOLLOUT) == EPOLLOUT)
     ...

我也尝试过:

if (events & EPOLLIN)
     ...
if (events & EPOLLOUT)
     ...

两种方式总是正确的!

然而,每当我在我的 epoll fd 上调用 epoll_wait 时,返回的活动文件描述符总是设置两个位,即使 send() 没有返回 EAGAIN,但是当我尝试 recv() 时它返回 EAGAIN。

我不知道当 recv() 返回 EAGAIN 时我应该做什么,我应该删除EPOLLOUT标志还是什么?

@Nikolai N Fetissov 要求的更多代码:

static int get_active_fd(events *evs, int index, sstate_t *flags)
{
    uint32_t events = evs->events[index].events;
    int fd = evs->events[index].data.fd;;

    if ((events & EPOLLERR) == EPOLLERR || (events & EPOLLHUP) == EPOLLHUP) {
        close(fd);
        return -1;
    }

    if (events & EPOLLIN)
        *flags |= DATA_IN;

    return fd;
}

void sockset_add(events *evs, int fd)
{
    struct epoll_event ev;
    ...
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    if (epoll_ctl(evs->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
        eprintf("sockset_add(): epoll_ctl(%d) returned an error %d(%s)\n",
                fd, errno, strerror(errno));
}

然后稍后我调用 epoll_wait():

if (flags & DATA_IN) {
       /* try to read which is impossible because this is never set.  */
4

1 回答 1

32

我想你把它弄反了。EPOLLOUT除非您从写入尝试中获得,否则不要包含EAGAIN,并在您成功将字节写入套接字时将其删除。

这基本上意味着只要套接字在内核发送缓冲区中有空间,套接字就始终是可写的。

编辑0:

您有两个数据流——输入输出

EPOLLIN您正在通过包含在标志中来等待输入。如果从该标志返回时epoll_wait(2)未设置,则某个事件发生在某个其他套接字上,或者该套接字有其他事件。将标志保留在事件中,除非您遇到错误(这意味着您仍然对套接字上的输入感兴趣)。

您不必等待输出(因为这是您的操作),您只需写入套接字,但如果溢出套接字发送缓冲区,您EAGAIN将从send(2)or获得write(2)。在这种情况下,您开始等待输出成为可能(内核耗尽套接字发送缓冲区,从而为您发送更多空间)包括EPOLLOUT. 一旦你得到它,将你的挂起输出字节写入套接字,如果你成功,EPOLLOUT从事件中删除。

现在EPOLLET表示边缘触发的等待,这意味着每次状态更改时您所需的事件只会发出一次信号(例如从“无输入”到“有输入”)。在这种模式下,您应该循环读取输入字节,直到获得EAGAIN.

希望这可以帮助。

于 2012-11-26T16:27:49.887 回答