单页网站如何制作,重庆有几个区几个县,有没有做美食的视频网站,毕设做网站答辩一般问什么1--epoll的优点 select()的缺点#xff1a; ① 调用 select() 函数后针对所有文件描述符的循环语句#xff1b; ② 调用 select() 函数时需要向操作系统传递监视对象信息#xff1b; epoll()的优点#xff1a; ① 无需编写以监视状态变化为目的的针对所有文件描述符的循环语…1--epoll的优点 select()的缺点 ① 调用 select() 函数后针对所有文件描述符的循环语句 ② 调用 select() 函数时需要向操作系统传递监视对象信息 epoll()的优点 ① 无需编写以监视状态变化为目的的针对所有文件描述符的循环语句 ② 调用 epoll_wait() 函数时无需每次传递监视对象信息 2--epoll的常用操作 epoll_create: 创建保存 epoll 文件描述符的空间 epoll_ctl: 向空间注册并注销文件描述符 epoll_wait: 等待文件描述符发生变化 #include sys/epoll.h
int epoll_create(int size);
// 成功时返回 epoll 文件描述符失败时返回 -1
// size 表示epoll实例的大小只是建议给操作系统的一个参考
// 调用 epoll_create 函数时创建的文件描述符保存空间称为epoll例程
#include sys/epoll.h
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
// 成功时返回0失败时返回-1
// epfd 表示 epoll 例程的文件描述符
// op 用于指定监视对象的添加、删除或更改等操作
// fd 表示需要注册的监视对象文件描述符
// event 表示监视对象的事件类型struct epoll_event event;
...
event.events EPOLLIN; // 发生需要读取数据的事件时
event.data.fd sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, event);
...
// event.events 常用的事件有
// EPOLL_IN 表示需要读取数据的情况
// EPOLLOUT 表示输出缓冲为空可以立即发送数据的情况
// EPOLLPRI 表示收到 OOB 数据的情况
// EPOLLRDHUP 表示断开连接或半关闭的情况常用于边缘触发方式
// EPOLLERR 表示发生错误的情况
// EPOLLET 表示以边缘触发的方式得到事件通知
// EPOLLONESHOT 表示发生一次事件后相应的文件描述符不再收到事件通知
// 通过位或运算可以同时传递上述多个参数 第二个参数 op 的常见常量和含义如下 ① EPOLL_CTL_ADD: 将文件描述符注册到 epoll 例程 ② EPOLL_CTL_DEL: 从 epoll 例程中删除文件描述符 ③ EPOLL_CTL_MOD: 更改注册的文件描述符的关注事件 epoll_ctl(A, EPOLL_CTL_ADD, B, C);
// 表示在 epoll 例程 A 中注册文件描述符 B主要目的是监视参数 C 中的事件epoll_ctl(A, EPOLL_CTL_DEL, B, NULL);
// 表示在 epoll 例程 A 中删除文件描述符 B
#include sys/epoll.h
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
// 成功时返回发生事件的文件描述符数
// epfd 表示 epoll 例程的文件描述符
// events 表示保存发生事件的文件描述符集合的结构体地址值
// maxevents 表示第二个参数可以保存的最大事件数
// timeout 表示以 ms 为单位的等待事件传递 -1 时表示一直等待直到事件发生
3--基于 epoll 的回声服务端
// gcc echo_epollserv.c -o echo_epollserv
// ./echo_epollserv 9190#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include sys/epoll.h#define BUF_SIZE 100
#define EPOLL_SIZE 50void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;socklen_t adr_sz;int str_len, i;char buf[BUF_SIZE];struct epoll_event *ep_events; struct epoll_event event; // 发生时间的文件描述符结构体int epfd, event_cnt;if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error);}if(listen(serv_sock, 5) -1){error_handling(listen() error);}epfd epoll_create(EPOLL_SIZE); // 创建保存 epoll 文件描述符的空间ep_events malloc(sizeof(struct epoll_event)*EPOLL_SIZE);event.events EPOLLIN; // 设置监视需要读取数据的情况event.data.fd serv_sock; // 设置监视的文件描述符epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, event); // 将 serv_sock 注册到 epoll 例程中while(1){event_cnt epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); // 等待事件的发生if(event_cnt -1){puts(epoll_wait() error);break;}for(i 0; i event_cnt; i){ // 遍历发生事件数if(ep_events[i].data.fd serv_sock){ // 当发生时间的文件描述符等于设置的 serv_sock 时adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, adr_sz);event.events EPOLLIN;event.data.fd clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, event);printf(connected client: %d \n, clnt_sock);}else{str_len read(ep_events[i].data.fd, buf, BUF_SIZE); // 接收数据if(str_len 0){ // 接收的数据是 EOF则关闭连接epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL); // 收到EOF删除注册的文件描述符close(ep_events[i].data.fd);printf(closed client: %d \n, ep_events[i].data.fd);}else{ // 将读取的数据返回给客户端实现回声的功能write(ep_events[i].data.fd, buf, str_len); // echo}}}}close(serv_sock);close(epfd);return 0;
}
4--条件触发和边缘触发 条件触发和边缘触发的区别在于发生事件的时间点 条件触发中只要输入缓冲有数据就会一直通知该事件 边缘触发中输入缓冲收到数据时仅注册 1 次该事件即使输入缓冲中还留有数据也不会再进行注册 epoll 默认以条件触发的方式工作 条件触发代码
// gcc echo_EPLTserv.c -o echo_EPLT_serv
// ./echo_EPLT_serv 9190#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include sys/epoll.h#define BUF_SIZE 4 // 减少缓冲大小阻止服务器一次性读取接收的数据验证条件触发
#define EPOLL_SIZE 50void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;socklen_t adr_sz;int str_len, i;char buf[BUF_SIZE];struct epoll_event *ep_events;struct epoll_event event;int epfd, event_cnt;if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error);}if(listen(serv_sock, 5) -1){error_handling(listen() error);}epfd epoll_create(EPOLL_SIZE);ep_events malloc(sizeof(struct epoll_event)*EPOLL_SIZE);event.events EPOLLIN;event.data.fd serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, event);while(1){// 条件触发中每次收到客户端数据都会调用 epoll_wait() 函数event_cnt epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); if(event_cnt -1){puts(epoll_wait() error);break;}puts(return epoll_wait);for(i 0; i event_cnt; i){if(ep_events[i].data.fd serv_sock){adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, adr_sz);event.events EPOLLIN;event.data.fd clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, event);printf(connected client: %d \n, clnt_sock);}else{str_len read(ep_events[i].data.fd, buf, BUF_SIZE); // 一次只能读取 4 个字节if(str_len 0){ // 收到 EOF 关闭连接epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);close(ep_events[i].data.fd);printf(closed client: %d \n, ep_events[i].data.fd);}else{write(ep_events[i].data.fd, buf, str_len); // echo}} }}close(serv_sock);close(epfd);return 0;
}在 event.events 中设置 EPOLLET 来设置边缘触发 在边缘触发中从客户端接收数据只会注册 1 次事件 边缘触发可以分离接收数据和处理数据的时间点 // gcc echo_EPETserv.c -o echo_EPETserv
// ./echo_EPETserv 9190#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include sys/epoll.h
#include fcntl.h
#include errno.h#define BUF_SIZE 4
#define EPOLL_SIZE 50void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}void setnonblockingmode(int fd){int flag fcntl(fd, F_GETFL, 0);fcntl(fd, F_SETFL, flag|O_NONBLOCK);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;socklen_t adr_sz;int str_len, i;char buf[BUF_SIZE];struct epoll_event *ep_events;struct epoll_event event;int epfd, event_cnt;if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error);}if(listen(serv_sock, 5) -1){error_handling(listen() error);}epfd epoll_create(EPOLL_SIZE);ep_events malloc(sizeof(struct epoll_event)*EPOLL_SIZE);setnonblockingmode(serv_sock);event.events EPOLLIN;event.data.fd serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, event);while(1){event_cnt epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);if(event_cnt -1){puts(epoll_wait() error);break;}puts(return epoll_wait);for(i 0; i event_cnt; i){if(ep_events[i].data.fd serv_sock){adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, adr_sz);setnonblockingmode(clnt_sock);event.events EPOLLIN|EPOLLET; // 设置边缘触发event.data.fd clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, event);printf(connected client: %d \n, clnt_sock);}else{while(1){ // 边缘触发中接收数据仅注册 1 次事件因此需要循环读取完输入缓冲中的所有数据str_len read(ep_events[i].data.fd, buf, BUF_SIZE);if(str_len 0){ // 接收到 EOF 后关闭连接epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);close(ep_events[i].data.fd);printf(closed client: %d \n, ep_events[i].data.fd);break;}else if(str_len 0){ // read 函数发现输入缓冲中没有数据可读时返回 -1同时errno中保存EAGAIN常量if(errno EAGAIN){break;}}else{write(ep_events[i].data.fd, buf, str_len); // echo}}}}}close(serv_sock);close(epfd);return 0;
}