多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] ## 概述 今天无意中发现了关于epoll的问题,事情是这样的,我在写关于epoll的echo服务器程序, 客户端连接上了之后,发消息,可以正常返回,但是当客户端连接断开后,服务端程序开始死循环,具体代码如下: ``` #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int socketfd = socket(AF_INET, SOCK_STREAM, 0); if (socketfd == -1) return -1; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(8088); if (bind(socketfd, (struct sockaddr_in*)&addr, sizeof addr) == -1) { printf("bind error\n"); return -1; } if (listen(socketfd, 1024) == -1) { printf("listen error\n"); return -1; } int epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { printf("epoll_fd error\n"); return -1; } struct epoll_event event; event.data.fd = socketfd; event.events = EPOLLIN; epoll_ctl(epoll_fd, EPOLL_CTL_ADD,socketfd, &event); while (1) { int nfds = epoll_wait(epoll_fd, &event, 1024, -1); if (nfds <= 0) { continue; } if (event.data.fd == socketfd) { struct sockaddr_in connaddr; socklen_t addr_len = sizeof(connaddr); int connfd = accept(socketfd, (struct sockaddr*)&connaddr, &addr_len); if (connfd == -1) { printf("connfd error\n"); return -1; } event.data.fd = connfd; event.events = EPOLLIN; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connfd, &event); }else { int connfd = event.data.fd; char buf[1024]; int n = recv(connfd, buf, 1024, 0); if (n <= 0) { printf("recv error\n"); continue; } buf[n] = '\0'; printf("recv msg: %s\n", buf); } } } ``` 编译&运行 ``` $ gcc -o server epoll.c $ ./server ``` 我使用的是 sokit当作客户端,真的很好用,安利一波。 * 客户端连接上8088端口,然后发送消息,正常。 * 断开客户端连接,服务器开始死循环不断的输出`printf("recv error\n");` 我在想,把监听的描述符替换成了连接描述符 `connfd`,断开客户端连接后,epoll_wait 应该检测不到有事件就绪了,应该是处于阻塞状态的。 我引用一段epoll的manual page: ``` > Will closing a file descriptor cause it to be removed from all epoll sets automatically? > > Yes, but be aware of the following point. A file descriptor is a reference to an open file description (see open(2)). Whenever a descriptor is duplicated via dup(2), dup2(2), fcntl(2) F\_DUPFD, or fork(2), a new file descriptor referring to the same open file description is created. An open file description continues to exist until all file descriptors referring to it have been closed. A file descriptor is removed from an epoll set only after all the file descriptors referring to the underlying open file description have been closed (or before if the descriptor is explicitly removed using epoll\_ctl(2) EPOLL\_CTL\_DEL). This means that even after a file descriptor that is part of an epoll set has been closed, events may be reported for that file descriptor if other file descriptors referring to the same underlying file description remain open. ``` 总结起来就是一句话,epoll接口操作的是文件描述符,而epoll的内核维护的是文件描述。 http://www.ilovecpp.com/2019/01/20/effective-epoll/