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