如何为socket或epoll事件分配上下文

时间:2013-09-29 13:03:39

标签: linux sockets networking signals epoll

我想使用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);”与此问题有关。

3 个答案:

答案 0 :(得分:1)

传递给epoll_ctl()的 epoll_event 结构,由epoll_wait()填充,具有 data_ptr 字段。

答案 1 :(得分:1)

正如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类型并调用默认方法来处理事件,无论它是什么。然后,类可以调用适当的方法来完成读/写等工作。

答案 2 :(得分:0)

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

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