epoll
epoll如何使用
1、epoll在服务器底层使用时,一定会和一个死循环一起。
2、关闭一个fd时,记得也要从epoll中删除。
close(clientfd);
ev.events = EPOLLIN;
ev.data.fd = clientfd;
epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);
3、
EPOLLIN触发条件:
处于可读状态。
从不可读状态变为可读状态。
EPOLLOUT触发条件:
EPOLLOUT触发条件:
处于可写状态。
从不可写状态变为可写状态。
”可读事件"表示有数据到来,"可写事件"表示内核缓冲区有剩余发送空间,“错误事件“表示socket发生了一些网络错误。
面试题:
fd可读可写的条件?
4、一个fd可能同时出现可读又可写状态,比如一个fd可写事件进行发送数据时,send返回-1,此时代表数据没发完,要继续发送。就要给ev.events |= EPOLLOUT,其他地方还会判断epollin,所以有同时in out出现的情况。
epoll的et与lt
LT:为水平触发,这个是默认的,recvbuf中有数据就会一直触发;
ET:为边缘触发,recvbuf中从无到有数据,才触发。
通常ET+循环使用,来处理小块数据。若处理大块数据时,会只处理这一个fd,耗时较长。
LT用来处理大块数据。
epoll的底层原理
红黑树+消息队列.
epoll的介绍,和使用函数?
答:eopll其实就是典型的io多路复用,最大的特点就是支持高并发。
Epoll_create():创建一个epoll对象,且内部初始化了红黑树和双向链表;
红黑树记录的是要观察监听事件的socket;
双向链表中记录的是所有有数据/有事件的socket。
Epoll_ctl():将socket和与此socket相关的事件添加到epoll对象中去,就可以通过epoll对象来监视这个socket上的数据往来;
EPOLL_CTL_ADD:等价于往红黑树中增加节点;
EPOLL_CTL_DEL:等价于从红黑树中删除节点;
EPOLL_CTL_MOD:等价于修改已有的红黑树节点;可增加减少读写事件
Epoll_wait():阻塞一小段时间等待事件发生,返回事件集合。其实就是从双向链表中把节点数据考出去,然后从链表中删除。
reactor
epoll的io管理,reactor,即反应堆。
Reactor 模式是用于同步 I/O,中心思想是将所有要处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上;
一旦有 I/O 事件到来或是准备就绪(文件描述符或 socket 可读、写),多路复用器返回并将事
先注册的相应 I/O 事件分发到对应的处理器中
Reactor 模型有三个重要的组件:
多路复用器:由操作系统提供,在 linux 上一般是 select, poll, epoll 等系统调用。
事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。epoll_wait
事件处理器:负责处理特定事件的处理函数。
如何封装成reactor
1、服务器端fd有两种,分别为:监听的fd和连接进来的fd,二者都要放入epoll进行监听;
2、
代码实现
struct sockitem { //可以存储半包数据
int sockfd; //
int (*callback)(int fd, int events, void *arg);
char recvbuffer[1024]; // recvbuf存储数据,避免数据没有接受完整
char sendbuffer[1024]; //
//
};
// mainloop / eventloop --> epoll -->
struct reactor { //与反应堆相关的全局变量
int epfd;
struct epoll_event events[512];
};
struct reactor *eventloop = NULL;
int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg) {
struct sockitem *si = (struct sockitem*)arg;
send(fd, "hello\n", 6, 0); //
//发送完后要将fd的状态设为原样
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
//ev.data.fd = clientfd;
si->sockfd = fd;
si->callback = recv_cb;
ev.data.ptr = si;
epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);
}
// ./epoll 8080
int recv_cb(int fd, int events, void *arg) {
//int clientfd = events[i].data.fd;
struct sockitem *si = (struct sockitem*)arg;
struct epoll_event ev;
char buffer[1024] = {0};
int ret = recv(fd, buffer, 1024, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { //
return -1;
} else {
}
ev.events = EPOLLIN;
//ev.data.fd = fd;
epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);
close(fd);
free(si);
} else if (ret == 0) { //
//
printf("disconnect %d\n", fd);
ev.events = EPOLLIN;
//ev.data.fd = fd;
epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);
close(fd);
free(si);
} else {
printf("Recv: %s, %d Bytes\n", buffer, ret);
// fd --> full
// fd epollout
//ret = send(fd, buffer, ret, 0);
//if (ret == -1) {
// }
//
//
//此处解析http、websocket协议数据
//当待发送的数据过大要多次send时,
struct epoll_event ev;
ev.events = EPOLLOUT | EPOLLET;
//ev.data.fd = clientfd;
si->sockfd = fd;
si->callback = send_cb;
ev.data.ptr = si;
epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);
}
}
int accept_cb(int fd, int events, void *arg) {
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(struct sockaddr_in));
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(fd, (struct sockaddr*)&client_addr, &client_len);
if (clientfd <= 0) return -1;
char str[INET_ADDRSTRLEN] = {0};
printf("recv from %s at port %d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port));
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; //accept接收后先关注客户端是否有数据进来,来处理,所以设置epollin可读
//ev.data.fd = clientfd;
struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
si->sockfd = clientfd;
si->callback = recv_cb;
ev.data.ptr = si;
epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, clientfd, &ev);
return clientfd;
}
void *worker_thread(void *arg) {
while (1) {
int nready = epoll_wait(eventloop->epfd, eventloop->events, 512, -1);
if (nready < -1) {
break;
}
// listen
//
int i = 0;
for (i = 0;i < nready;i ++) {
if (eventloop->events[i].events & EPOLLIN) {
//printf("sockitem\n");
struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
si->callback(si->sockfd, eventloop->events[i].events, si);
}
if (eventloop->events[i].events & EPOLLOUT) {
struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
si->callback(si->sockfd, eventloop->events[i].events, si);
}
}
}
}
int main(int argc, char *argv[]) {
if (argc < 2) {
return -1;
}
int port = atoi(argv[1]);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
return -2;
}
if (listen(sockfd, 5) < 0) {
return -3;
}
eventloop = (struct reactor*)malloc(sizeof(struct reactor));
// epoll opera
eventloop->epfd = epoll_create(1);
struct epoll_event ev;
ev.events = EPOLLIN;
//ev.data.fd = sockfd; //int idx = 2000;
struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
si->sockfd = sockfd;
si->callback = accept_cb;
ev.data.ptr = si;
epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, sockfd, &ev);
//主线程负责accept,子线程负责clientfd
pthread_t id;
pthread_create(&id, NULL, worker_thread, NULL);
//pthread_cond_waittime();
while(1) {
}
}