C++ C++;被Linux系统和异步线程上的epoll和socket fd弄糊涂了

C++ C++;被Linux系统和异步线程上的epoll和socket fd弄糊涂了,c++,linux,sockets,asynchronous,epoll,C++,Linux,Sockets,Asynchronous,Epoll,我试图理解如何将epoll库(Linux)与套接字文件描述符结合使用。据我所知,网上关于epoll的信息有限。到目前为止,我发现的最有用的资源是: 它给出了一个完整的epoll工作示例,但是它与stdin文件描述符一起使用。(我在别处读到EPOL不能与stdin、stdout和stderr一起使用。我认为信息是错误的。) 我已经试着用MWE来建立这个 #include <string> #include <sstream> #include <iostream&g

我试图理解如何将epoll库(Linux)与套接字文件描述符结合使用。据我所知,网上关于epoll的信息有限。到目前为止,我发现的最有用的资源是:

它给出了一个完整的epoll工作示例,但是它与
stdin
文件描述符一起使用。(我在别处读到EPOL不能与
stdin
stdout
stderr
一起使用。我认为信息是错误的。)

我已经试着用MWE来建立这个

#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <string.h>
#include <unistd.h>
#include <future>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>


int thread_function(int accept_sock_fd)
{
    char *read_buffer = new char[1024];

    unsigned int read_length = 0;
    std::string read_string = "";
    for(;;)
    {
        unsigned int t_read_length = read(accept_sock_fd, read_buffer, 1024 - 1);
        //std::cout << "t_read_length=" << t_read_length << std::endl;
        //std::cout << read_buffer << std::endl;
        read_string += std::string(read_buffer);
        read_length += t_read_length;

        // dodgy ?
        // tries to detect end of data using new lines
        // but what if client does not send new line char?
        if(read_buffer[t_read_length - 1] == '\n')
        {
            break;
        }
    }
    std::cout << read_string << std::endl;
    //std::cout << read_length << std::endl;
    //std::cout << read_buffer << std::endl;

    std::string status_line = "HTTP/1.0 200 OK\r\n\r\n";
    std::string html_lines = "Hello World\r\n\r\n";
    std::string write_string = status_line + html_lines;
    //std::cout << write_string << std::endl;
    unsigned int write_length = write(accept_sock_fd, write_string.data(), write_string.length());
    //std::cout << "write_length=" << write_length << std::endl;
    close(accept_sock_fd);

    delete [] read_buffer;

    return 0;
}


int main()
{

    // read configuration file
    //Config config;
    //config.ReadFromFile("config.txt");
    //std::cout << config << std::endl;

    unsigned int num_threads = 4; //config.GetNumThreads();


    ///////////////////////////////////////////////////////////////////////////
    // init epoll
    //
    //

    int epoll_fd = epoll_create(num_threads);
    if(epoll_fd < 0)
    {
        std::cout << "epoll error" << std::endl;
    }

    struct epoll_event event;
    struct epoll_event *events = new struct epoll_event[num_threads * sizeof(struct epoll_event)];
    


    ///////////////////////////////////////////////////////////////////////////
    // create socket fd
    //
    //

    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_fd == 0)
    {
        std::cout << "Error creating sock_fd" << std::endl;
    }
//    int setsockopt(sockfd, 

    struct sockaddr_in server_address;
    memset(&server_address, 0, sizeof(struct sockaddr_in));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons(55555 /*config.GetPort()*/);

    bind(sock_fd, (struct sockaddr *)&server_address, sizeof(server_address));
    if(bind < 0)
    {
        std::cout << "Error: bind" << std::endl;
    }

    if(listen(sock_fd, 10) < 0)
    {
        std::cout << "Error: listen" << std::endl;
    }


    ///////////////////////////////////////////////////////////////////////////
    // continue epoll setup
    //
    //

    event.events = EPOLLIN; // | EPOLLPRI | EPOLLERR | EPOLLHUP;
    event.data.fd = 0; //client_sock;
    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &event)) // not sure about this
    {
        std::cout << "Failed to add epoll" << std::endl;
        return 1;
    }


    for(;;)
    {
        int n_fd = epoll_wait(epoll_fd, events, num_threads, 0);
        if(n_fd < 0)
        {
            std::cout << "die" << std::endl;
            break;
        }

        for(int i = 0; i < n_fd; ++ i)
        {
            int fd = events[i].data.fd;
            
            // accpt connection
            int accept_sock_fd = 0;
            struct sockaddr client_address;
            int addrlen = sizeof(client_address);
            accept_sock_fd = accept(fd, NULL, NULL); // not sure what goes here
            accept_sock_fd = accept(sock_fd, NULL, NULL); // not sure what goes here
            if(accept_sock_fd < 0)
            {
                std::cout << "Error: accept" << std::endl;
            }

            // do something to handle data on accept_sock_fd
            std::future<int> thread_future = std::async(thread_function, accept_sock_fd);
            int ret = thread_future.get();
            std::cout << ret << std::endl;

            // close connection
            close(accept_sock_fd);
            
        }
    }


    delete [] events;

    close(sock_fd);


    if(close(epoll_fd))
    {
        std::cout << "error: failed to close epoll fd" << std::endl;
    }


    return 0;
}

假设您已经打开了服务器套接字,您可以使用下面的代码。它基于
man epoll
可访问性

//假设您已正确打开listen\u sock
//int listen_sock;
// 
int epollfd=epoll_create1(0);
如果(epollfd==-1){
出口(1);
}
结构epoll_事件ev;
ev.events=依泊林;
ev.data.fd=监听_sock;
if(epoll\u ctl(epollfd、epoll\u ctl\u ADD、listen\u sock和ev)=-1){
出口(2);
}
对于(;;){
#定义最大事件64
结构epoll_事件事件[最大事件];
int events_count=epoll_wait(epollfd,events,MAX_events,-1);
如果(n==-1){
出口(3);
}
对于(int n=0;n
使用
epoll
与使用
poll
没有太大区别。棘手的事情是跟踪哪个客户机拥有epoll返回的文件描述符(如果有很多客户机)


如何使用
epoll
异步处理套接字文件(在本例中为TCP套接字服务器)。
  • 使用
    epoll\u create(2)
    打开一个epoll文件描述符
  • 使用
    socket(2)
    bind(2)
    listen(2)
    创建TCP套接字
  • 使用
    epoll\u ctl
    +
    epoll\u ctl\u Add
    将主TCP套接字文件描述符添加到epoll
  • 在循环内调用
    epoll\u wait
    ,程序将在
    epoll\u wait
    上睡眠,当有来自受监控文件描述符的事件时,内核将唤醒程序或当达到超时时
  • 如果
    epoll\u wait
    返回的值大于零,则需要确定epoll返回的文件描述符

  • 5.1。如果它是主TCP文件描述符,则需要
    接受(2)
    。然后将由
    accept(2)
    返回的客户端文件描述符添加到带有
    epoll\u ctl
    +
    epoll\u ctl\u add
    的epoll中。其他

    5.2。如果它是客户机文件描述符,那么您需要调用
    recv(2)
    并对该客户机执行任何操作

    在步骤5.2,如果在
    事件[i].events
    中看到
    EPOLLHUP
    ,则客户端已关闭其连接,您需要调用
    epoll\u ctl
    +
    epoll\u ctl\u DEL
    并关闭客户端文件描述符(不调用
    epoll\u ctl
    +
    epoll\u ctl\u DEL
    并关闭客户端文件描述符可能是安全的,但我更愿意先从epoll中删除它)


  • 转到第4步
  • 有关如何确定哪个客户机拥有epoll返回的文件描述符的详细机制,请参见下面的
    server.c
    code


    流程图 流程图可以使它更清楚。


    你可以在家尝试的工作示例 服务器.c
    /*
    * https://stackoverflow.com/questions/66916835/c-confused-by-epoll-and-socket-fd-on-linux-systems-and-async-threads
    */
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义PRERF“(错误号=%d)%s\n”
    #定义前置(NUM)NUM、strerror(NUM)
    #定义EPOLL\u映射到\u NOP(0u)
    #定义EPOLL\u MAP\u SHIFT(1u)/*SHIFT以覆盖保留值MAP\u到NOP*/
    结构客户端\u插槽{
    使用布尔;
    国际客户联合会;
    char src_ip[sizeof(“xxx.xxx.xxx.xxx”);
    uint16\u t src\u端口;
    uint16_t我的索引;
    };
    结构tcp_状态{
    布尔停止;
    int-tcp_-fd;
    int epoll_fd;
    uint16_t客户_c;
    struct client_slot clients[10];
    /*
    *将文件描述符映射到客户端\u插槽数组索引
    *注意:我们假设没有大于10000的文件描述符。
    *
    *你必须在生产中调整这个。
    */
    uint32客户地图[10000];
    };
    静态int my_epoll_add(int epoll_fd、int fd、uint32_t事件)
    {
    INTERR;
    结构epoll_事件;
    /*闭嘴*/
    memset(&event,0,sizeof(struct-epoll_-event));
    event.events=事件;
    event.data.fd=fd;
    if(epoll\u ctl(epoll\u fd、epoll\u ctl\u ADD、fd和event)<0){
    err=errno;
    printf(“epoll_ctl(epoll_ctl_ADD):“PRERF,PREAR(err));
    返回-1;
    }
    返回0;
    }
    静态int my_epoll_delete(int epoll_fd,int fd)
    {
    INTERR;
    if(epoll\u ctl(epoll\u fd,epoll\u ctl\u DEL,fd,NULL)<0){
    err=errno;
    printf(“epoll_ctl(epoll_ctl_DEL):“PRERF,PREAR(err));
    返回-1;
    }
    返回0;
    }
    静态常量char*convert\u addr\u ntop(结构sockaddr\u in*addr,char*src\u ip\u buf)
    {
    INTERR;
    常量字符*ret;
    in\u addr\u t saddr=addr->sin\u addr.s\u addr;
    ret=inet_ntop(AF_inet,&saddr,src_ip_buf,sizeof(“xxx.xxx.xxx.xxx”);
    if(ret==NULL){
    err=errno;
    err=err?err:EINVAL;
    printf(“inet_ntop():”PRERF,PREAR(err));
    返回NULL;
    }
    返回ret;
    }
    静态int-accept\u-new\u客户端(int-tcp\u-fd,struct-tcp\u-state*state)
    {
    INTERR;
    国际客户联合会;
    地址中的结构sockaddr\u;
    socklen\u t addr\u len=sizeof(addr);
    uint16\u t src\u端口;
    const char*src_ip;
    char src_ip_buf[sizeof(“xxx.xxx.xxx.xxx”);
    常量大小\u t客户端\u插槽
    
    // correct fd
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, /*the_server_socket_fd*/, &event);
    ...
    // not sure about this one, particularly the waiting time of 0
    for(;;)
        epoll_wait(epoll_fd, events, /*num_threads*/, 0);
        
        for(i ...)
            fd = events[i].data.fd; // what fd is this? what does it mean?
            accept(/*something here, but what*/ fd OR the_server_socket_fd ?, NULL, NULL);
            // launch thread with retuned value from accept, probably
    
    // assume you have opened listen_sock properly
    // int listen_sock;
    // 
    int epollfd = epoll_create1(0);
    if (epollfd == -1) {
      exit(1);
    }
    
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
      exit(2);
    }
    
    for (;;) {
      #define MAX_EVENTS 64
      struct epoll_event events[MAX_EVENTS];
      int events_count = epoll_wait(epollfd, events, MAX_EVENTS, -1);
      if (n == -1) {
        exit(3);
      }
    
      for (int n = 0; n < events_count; ++ n) {
        if (events[n].data.fd == listen_sock) {
          struct sockaddr_un addr;
          socklen_t addrlen;
          int socket = accept(listen_sock, (struct sockaddr *) &addr, &addrlen);
        }
      }
    }
    
    ammarfaizi2@integral:~$ for i in {1..10}; do php test.php & done;
    [1] 14214
    [2] 14215
    [3] 14216
    [4] 14217
    [5] 14218
    [6] 14219
    [7] 14220
    [8] 14221
    [9] 14222
    [10] 14223
    ammarfaizi2@integral:~$ 
    
    ammarfaizi2@integral:~/ex$ ./server
    Initializing epoll_fd...
    Creating TCP socket...
    Listening on 0.0.0.0:1234...
    Entering event loop...
    Client 127.0.0.1:60866 has been accepted!
    Client 127.0.0.1:60866 sends: "AAAAAAAA"
    Client 127.0.0.1:60868 has been accepted!
    Client 127.0.0.1:60868 sends: "AAAAAAAA"
    Client 127.0.0.1:60870 has been accepted!
    Client 127.0.0.1:60872 has been accepted!
    Client 127.0.0.1:60870 sends: "AAAAAAAA"
    Client 127.0.0.1:60872 sends: "AAAAAAAA"
    Client 127.0.0.1:60874 has been accepted!
    Client 127.0.0.1:60874 sends: "AAAAAAAA"
    Client 127.0.0.1:60876 has been accepted!
    Client 127.0.0.1:60878 has been accepted!
    Client 127.0.0.1:60878 sends: "AAAAAAAA"
    Client 127.0.0.1:60876 sends: "AAAAAAAA"
    Client 127.0.0.1:60880 has been accepted!
    Client 127.0.0.1:60880 sends: "AAAAAAAA"
    Client 127.0.0.1:60882 has been accepted!
    Client 127.0.0.1:60882 sends: "AAAAAAAA"
    Client 127.0.0.1:60884 has been accepted!
    Client 127.0.0.1:60884 sends: "AAAAAAAA"
    Client 127.0.0.1:60866 sends: "BBBBBBBB"
    Client 127.0.0.1:60868 sends: "BBBBBBBB"
    Client 127.0.0.1:60870 sends: "BBBBBBBB"
    Client 127.0.0.1:60872 sends: "BBBBBBBB"
    Client 127.0.0.1:60874 sends: "BBBBBBBB"
    Client 127.0.0.1:60878 sends: "BBBBBBBB"
    Client 127.0.0.1:60876 sends: "BBBBBBBB"
    Client 127.0.0.1:60880 sends: "BBBBBBBB"
    Client 127.0.0.1:60882 sends: "BBBBBBBB"
    Client 127.0.0.1:60884 sends: "BBBBBBBB"
    Client 127.0.0.1:60866 sends: "CCCCCCCC"
    Client 127.0.0.1:60868 sends: "CCCCCCCC"
    Client 127.0.0.1:60870 sends: "CCCCCCCC"
    Client 127.0.0.1:60872 sends: "CCCCCCCC"
    Client 127.0.0.1:60874 sends: "CCCCCCCC"
    Client 127.0.0.1:60878 sends: "CCCCCCCC"
    Client 127.0.0.1:60876 sends: "CCCCCCCC"
    Client 127.0.0.1:60880 sends: "CCCCCCCC"
    Client 127.0.0.1:60882 sends: "CCCCCCCC"
    Client 127.0.0.1:60884 sends: "CCCCCCCC"
    Client 127.0.0.1:60866 has closed its connection
    Client 127.0.0.1:60868 has closed its connection
    Client 127.0.0.1:60870 has closed its connection
    Client 127.0.0.1:60872 has closed its connection
    Client 127.0.0.1:60874 has closed its connection
    Client 127.0.0.1:60878 has closed its connection
    Client 127.0.0.1:60876 has closed its connection
    Client 127.0.0.1:60880 has closed its connection
    Client 127.0.0.1:60882 has closed its connection
    Client 127.0.0.1:60884 has closed its connection