socket编程--EMFILE错误
#### EMFILE该错误表示该进程可使用的文件描述符超出了上限,默认为1024
在select、poll、epoll的LT模式如果不处理这种错误,会导致处于busy loop状态
##### 如何处理EMFILE?
1、修改上限值、治标不至本
2、死等
3、关闭listen_fd
4、epoll改用ET模式
5、退出程序
6、准备一个空闲的描述符,先把位置站好,该描述符最开始一定是3,0 1 2 属于标准输入、输出、错误
然后连接到来,close掉空闲描述符,腾出3号,再次accept,再次close这个连接,再open获取3号描述符
这样我们就优雅的关闭了该连接,要不只能退出程序,否则accept会返回-1 ,poll、epoll的LT模式会认为你没有处理该 事件 就会一直触发 处于busy loop状态
服务端测试
```98
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <poll.h>
#include <vector>
#include <netinet/in.h> // INADDR_ANY
#include <errno.h>
#include <arpa/inet.h> // inet_ntoa
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
// 先提前占用一个文件描述符,是3对吧,0 1 2 = 标准输入、输出、错误
int idleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
struct sockaddr_in server_addr {};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5578);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
bind(listen_fd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
listen(listen_fd, 1);
struct pollfd read_pollfd;
read_pollfd.fd = listen_fd;
read_pollfd.events = POLLIN;
read_pollfd.revents= 0;
std::vector<pollfd> vec_read_poll;
vec_read_poll.push_back(read_pollfd);
int read_ready;
int connected_fd;
struct sockaddr_inclient_addr;
socklen_t addr_len = sizeof(client_addr);
int saveErrno = -1;
while (1)
{
read_ready = poll(vec_read_poll.data(), vec_read_poll.size(), -1);
printf("read_ready = %d\n", read_ready);
// poll失败
if (read_ready == -1)
{
if (errno == EINTR)
continue;
return -1;
}
else if (read_ready == 0)
continue;
else if (vec_read_poll.revents & POLLIN)
{
--read_ready;
connected_fd = accept4(listen_fd, (sockaddr *)&client_addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
printf("connected_fd = %d\n", connected_fd);
saveErrno = errno;
if (connected_fd == -1)
{
if (saveErrno == EINTR)
continue;
else if (saveErrno == EMFILE)
{
printf("该服务器连接上限,请过一会在连接\n");
// 立即关闭3号文件描述符
close(idleFd);
// 腾出3号,再次accept
idleFd = accept4(listen_fd, nullptr, nullptr,SOCK_NONBLOCK | SOCK_CLOEXEC);
// 优雅的关闭该连接,要不只能退出程序,否则accept会返回-1
// poll是lt模式会认为你没有处理该事件 就会一直触发 处于busy loop状态
close(idleFd);
// 再次占用3号
idleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
continue;
}
else if (saveErrno == ECONNABORTED)
{
close(connected_fd);
continue;
}
else
return -1;
}
std::cout << "client_ip = " << inet_ntoa(client_addr.sin_addr) << std::endl;
std::cout << "client_port = " << ntohs(client_addr.sin_port) << std::endl;
if (read_ready == 0)
continue;
}
}
return 0;
}
```
客户端fork 1019个连接,再打开telnet连接就会触发EMFILE错误
```
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <poll.h>
#include <vector>
#include <netinet/in.h> // INADDR_ANY
#include <errno.h>
#include <arpa/inet.h> // inet_ntoa
#include <cstring>
#include <unistd.h>
int main(void)
{
int ret = -1;
// 0 1 2 3 4 输入、输出、错误、idleFd、listen_fd
for (int i = 0; i < 1019; i++)
{
ret = fork();
usleep(100);
if (ret == 0)
{
// 创建socket 非阻塞 | 子进程中关闭该套接字
int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
// 初始化socket信息
struct sockaddr_in server_addr {};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5578);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(server_addr);
int conn = connect(listen_fd, (struct sockaddr *)&server_addr, len);
getchar();
}
}
getchar();
return 0;
}
```
这样就限制死了客户端的连接数量,当然可以创建一个等待队列,监测其他客户端关闭,就可以accpet了。
页:
[1]