在侦听套接字上,我设置了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时,返回的活动文件描述符ALWAYS都设置了两个位,即使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. */
答案 0 :(得分:27)
我认为你倒退了。除非您从写入尝试中获得EPOLLOUT
,否则请不要包含EAGAIN
,并在成功将字节写入套接字时将其删除。
这基本上意味着只要套接字内核发送缓冲区中存在空间,套接字总是可写。
您有两个数据流 - 输入和输出。
您正在等待输入,方法是在标志中加入EPOLLIN
。如果从epoll_wait(2)
返回该标志未设置,则某个事件发生在某个其他套接字上,或者此套接字有其他事件。除非出现错误(意味着您仍然对套接字上的输入感兴趣),请将事件中的标志保留在事件中。
您不必等待输出(因为它是您的操作),您只需写入套接字,但如果您溢出套接字发送缓冲区,您将从EAGAIN
获得send(2)
或write(2)
。在这种情况下,您可以通过包含EPOLLOUT
来开始等待输出(内核耗尽套接字发送缓冲区,从而为您发送更多内容)。完成后,将待处理输出字节写入套接字,如果成功,请从事件中删除EPOLLOUT
。
现在EPOLLET
表示边缘触发等待,这意味着您的所需事件只会在每次状态更改时发出一次信号(例如从“无输入”到“有输入”)。在此模式下,您应该在循环中读取输入字节,直到获得EAGAIN
。
希望这有帮助。