吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1477|回复: 0
收起左侧

[C&C++ 转载] socket编程--EMFILE错误

[复制链接]
古月不傲 发表于 2021-1-31 17:10
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状态

服务端测试

#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_in  client_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[0].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了。

免费评分

参与人数 2吾爱币 +12 热心值 +2 收起 理由
苏紫方璇 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
闲月疏云 + 2 + 1 有点一四

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 19:50

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表