如何分配一个上下文插座或epoll的事件(how to assign a context to a

2019-10-18 17:21发布

我想用epoll的编写基于事件服务器。

每个客户端都有一个不同的请求,服务器必须回应他们。 服务器将等待连接,当连接可用他们正在排队等待读取。 数据从客户端读取,他们会排队等待写入。 处理数据之后,适当的响应应该被发送到每一个。

所有的操作将是异步的。

问题是,我怎么能确定,它的响应是其插座时,插座都准备好写? 一种方式,我可以存储(套接字,数据)元组,但它是一种不好的编程。

我想如果我能分配一个上下文每个插座或每个epoll的事件,所以我能确定哪些数据属于哪个插座。

任何想法?

有没有使用,而不是epoll的SIGIO有什么建议? 如果我可以分配一个上下文的文件描述符,或信号(我不熟悉Linux编程),那么我可能会无限期地睡觉,等待信号...

现在忘了联网,看看下面这个例子,我打开一个预先创建的FIFO,并暂停该线程,直到我得到一个SIGIO,在另一种情况下,考虑我开了10 FIFO S和分配的一个随机数,以每,当我想打印数量安慰,不知何故,我必须能够检索数量,也许我可以指定一个上下文文件描述符?

#include <stdlib.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
static void sigioHandler(int sig)
{
}

int main()
{
    int fd, epfd, ret, i, nr_events, flags;
    struct sigaction sa;
    struct epoll_event event, *events;
    char buf[10];
    memset(buf, 0, 10);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = sigioHandler;
    if (sigaction(SIGIO, &sa, NULL) == -1)
    {
        perror("sigaction");
        exit(1);
    }
    events = malloc (sizeof (struct epoll_event) * 10);
    if (!events) {
          perror ("malloc");
          return 1;
  }

    fd = open("/tmp/foo", O_RDONLY);

    if(fcntl(fd, F_SETOWN, getpid())==-1){
        perror("own");
        exit(1);
    }
    flags = fcntl(fd, F_GETFL);
    if(fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK)==-1){
        perror("set");
        exit(1);
    }
    read(fd, buf, 10);
    epfd = epoll_create(10);
    if(epfd<0)
        perror("epoll_create");

    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;

    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
    if(ret)
        perror("epol_ctl");
    while(1){
        pause();
        nr_events = epoll_wait (epfd, events, 10, -1);
        if (nr_events < 0) {
            perror ("epoll_wait");
            free (events);
            return 1;
        }

        for (i = 0; i < nr_events; i++) {
            if(events[i].events & EPOLLIN)
            {
                read(events[i].data.fd, buf, 10);
                if(buf[0] == '#')
                    goto end;
                printf("%s", buf);
            }
        }
    }
end:
    free (events);

    close(epfd);
    close(fd);
    return 0;
}

改变一点点:

static void sigioHandler(int status, siginfo_t *ioinfo, void * context)
{
    if(ioinfo == NULL)
        return;

    switch (ioinfo->si_code)
    {
        case POLL_IN:
            printf("signal received for input chars.sig:%d -%d\n",status, ioinfo->si_code);
            break;

        case POLL_OUT:
        default:
            printf("signal received for something else.sig:%d -%d\n",status, ioinfo->si_code);
            break;
    }
}

in main:
...
sa.sa_sigaction = sigioHandler;
...

我得到一个奇怪的分段错误。

不知道FreeBSD的 “mac_set_fd(INT FD,mac_t标签);” 与此有关的问题。

Answer 1:

将传递给epoll_ctl(),和其中所述epoll_event结构由epoll_wait()填充,具有DATA_PTR字段。



Answer 2:

作为贝尼托指出传递到epoll_ctl的epoll_event结构具有DATA_PTR场。 为了完全正确的与您创建上下文中的字段定义为

typedef union epoll_data 
    void        *ptr;
    int          fd;
    uint32_t     u32;
    uint64_t     u64;
} epoll_data_t;

即工会。 通常情况下,FD字段用于在许多例子中的上下文。 但我已经找到了PTR场是最使用C ++中它可以用来指向包含套接字或连接的所有状态的对象。

所以,如果你有一个喜欢的对象:

class Connection
{
  private:
    int m_fd;

  public:
    Connection(int fd) : m_fd(fd) {}

    // Other methods like Send/Receive
};


/////
// Somewhere in your server accept loop for a server
struct sockaddr sin;
socklen_t len = sizeof(sin);
int fd = accept(s_listen, &sin, &len);
if (fd == -1) {
    // handle error
}

// or if it's a client you'd have just created the socket with a call to socket()

// and then you add the new connection to epoll.
epoll_event ev;
ev.data.ptr = new Connection(fd);
ev.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;    
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
    ; // handle error
}

后来,当你得到epoll的事件与epoll_wait,您可以访问event.data.ptr然后将其丢回连接类型并调用默认的方法来处理不管它是什么事件。 然后,类可以调用适当的方法来完成的工作,如读/写。



Answer 3:

关于你的第一个问题,我已经试过这样的事情,它的工作。 对于每一个你与其他同行的连接(假设你正在使用TCP),你有一个新的文件描述符。 因此,每个连接是由一个描述符是唯一的。

在项目中,我已经使用选择。 你必须添加描述它们使用的connect()FD_SET的结果。 欲了解更多信息,请参阅的选择Linux的手册页。



文章来源: how to assign a context to a socket or an epoll event