2

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

每个客户端都有一个不同的请求,服务器应该响应它们。服务器将等待连接,当连接可用时,它们将排队等待读取。从客户端读取数据,它们将排队等待写入。处理数据后,应向每个人发送适当的响应。

所有操作都将是异步的。

问题是,当套接字准备好写入时,我如何确定哪个响应是针对哪个套接字的?一种方式,我可以存储一个(套接字,数据)元组,但这是一种糟糕的编程。

我想知道是否可以为每个套接字或每个 epoll 事件分配一个上下文,以便确定哪些数据属于哪个套接字。

任何想法?

关于使用 SIGIO 代替 epoll 有什么建议吗?如果我可以将上下文分配给文件描述符或信号(我不熟悉 linux 编程),那么我可以无限期地休眠并等待信号......

现在忘记网络,看看这个例子,我打开一个预先创建的 FIFO,并暂停线程直到我得到一个 SIGIO,在另一种情况下,考虑我打开 10 个 FIFO 并为每个分配一个随机数,当我想将该号码打印到控制台,以某种方式我必须能够检索该号码,也许我可以为文件描述符分配一个上下文?

#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 label);” 与这个问题有关。

4

3 回答 3

2

您传递给 epoll_ctl() 并由 epoll_wait() 填充的 epoll_event 结构有一个data_ptr字段

于 2013-11-04T08:15:44.757 回答
2

正如 Benito 指出的那样,传递给 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_wait 获取 epoll 事件时,您可以访问 event.data.ptr 并将其转换回 Connection 类型并调用默认方法来处理事件。然后该类可以调用适当的方法来完成读/写等工作。

于 2014-06-12T03:55:59.783 回答
0

关于你的第一个问题,我已经尝试过这样的事情并且它奏效了。对于您与其他对等方的每个连接(假设您使用 TCP),您都有一个新的文件描述符。所以每个连接都是唯一的描述符。

在我的项目中,我使用了 select。您必须使用 FD_SET 添加作为 connect() 结果的描述符。有关详细信息,请参阅 select 的 Linux 手册页。

于 2013-09-29T19:28:25.297 回答