我正在使用 C 中的 epoll 编写一个状态机。我正在跟踪一个文件描述符,它是一个接受连接的 tcp 套接字,以及连接的文件描述符。在第一次读取和写入事件之后,epoll 发出多个
EPOLLIN
事件,这些事件将阻塞在 read
调用中。
这是代码现在的简化示例:
int main()
{
// omitted socket, bind, listen, epoll_create for length
num = epoll_wait(epollfd, events, maxev, -1);
for (i = 0; i < num; i++)
{
struct epoll_event event = events[i];
if (event.data.fd == sfd)
{
// accept and make socket non blocking
int cfd = accpet_connection(event.data.fd);
struct epoll_event ee = { 0 };
ee.data.fd = cfd;
ee.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, cfd, &ee);
}
else
{
if (event.events & EPOLLIN)
{
// read from fd function called
event.events |= EPOLLOUT;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
if (event.events & EPOLLOUT)
{
// write to fd function called
event.events |= EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
}
}
return 0;
}
当
read
返回0时,使用EPOLL_CTL_DEL
从epoll实例中删除fd并关闭文件描述符。
我的问题是,在第一次写入发生后,epoll 会不断说我有一个读取事件。当我从文件描述符中读取时,-1 返回
EAGAIN
或 EWOULDBLOCK
使用的读取函数如下所示:
ssize_t read_from_fd(int fd)
{
ssize_t n;
printf("read called\n");
n = read(fd, buf, MAX_BUF);
if (n == -1)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
printf("read blocked\n");
}
return -1;
}
if (n == 0)
{
// remove fd from epoll instance and close fd
}
}
为了演示输出,服务器启动,客户端连接,客户端发送一条消息,客户端读取一条消息,然后客户端关闭连接。这是输出:
$ ./server
accepted connection <fd>
read called
write called
read called
read blocked
read called
closed connection with <fd>
有时会有多个“read called”和“read blocked”日志。数量从 1 到 ~25 不等。
当我删除在写调用后调用的
epoll_ctl
函数时,我从 epoll 发出了多个“写”事件,并且程序因 SIGPIPE 而崩溃,因为客户端在收到一条消息后关闭了连接。