1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <string.h> #include <fcntl.h>
int main() { int epfd, sockfd; struct epoll_event ev; printf("=== Epoll_ctl 函数示例 ===\n"); // 创建epoll实例 epfd = epoll_create1(EPOLL_CLOEXEC); if (epfd == -1) { perror("epoll_create1 失败"); exit(EXIT_FAILURE); } printf("创建epoll实例: %d\n", epfd); // 创建测试用的socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("创建socket失败"); close(epfd); exit(EXIT_FAILURE); } printf("创建测试socket: %d\n", sockfd); // 设置socket为非阻塞模式 int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); printf("设置socket为非阻塞模式\n"); // 示例1: 添加文件描述符到epoll监视集合 printf("\n示例1: 添加文件描述符到epoll\n"); ev.events = EPOLLIN | EPOLLOUT | EPOLLET; // 读事件、写事件、边缘触发 ev.data.fd = sockfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { perror("EPOLL_CTL_ADD 失败"); } else { printf("成功添加socket %d 到epoll监视集合\n", sockfd); printf("监视事件: EPOLLIN | EPOLLOUT | EPOLLET\n"); } // 示例2: 修改已监视的文件描述符 printf("\n示例2: 修改已监视的文件描述符\n"); ev.events = EPOLLIN | EPOLLET; // 只监视读事件和边缘触发 ev.data.fd = sockfd; if (epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev) == -1) { if (errno == ENOENT) { printf("修改失败,文件描述符未在监视集合中: %s\n", strerror(errno)); } else { perror("EPOLL_CTL_MOD 失败"); } } else { printf("成功修改socket %d 的监视设置\n", sockfd); printf("新监视事件: EPOLLIN | EPOLLET\n"); } // 示例3: 删除文件描述符 printf("\n示例3: 删除文件描述符\n"); if (epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL) == -1) { if (errno == ENOENT) { printf("删除失败,文件描述符不在监视集合中: %s\n", strerror(errno)); } else { perror("EPOLL_CTL_DEL 失败"); } } else { printf("成功从epoll监视集合中删除socket %d\n", sockfd); } // 示例4: 错误处理演示 printf("\n示例4: 错误处理演示\n"); // 使用无效的epoll文件描述符 if (epoll_ctl(-1, EPOLL_CTL_ADD, sockfd, &ev) == -1) { if (errno == EBADF) { printf("无效epoll文件描述符错误处理正确: %s\n", strerror(errno)); } } // 使用无效的操作类型 if (epoll_ctl(epfd, 999, sockfd, &ev) == -1) { if (errno == EINVAL) { printf("无效操作类型错误处理正确: %s\n", strerror(errno)); } } // 使用无效的文件描述符 if (epoll_ctl(epfd, EPOLL_CTL_ADD, -1, &ev) == -1) { if (errno == EBADF) { printf("无效文件描述符错误处理正确: %s\n", strerror(errno)); } } // 示例5: epoll_event结构说明 printf("\n示例5: epoll_event结构说明\n"); printf("struct epoll_event 结构体:\n"); printf(" uint32_t events; // 事件类型\n"); printf(" epoll_data_t data; // 用户数据\n\n"); printf("常用事件类型:\n"); printf(" EPOLLIN: 可读事件\n"); printf(" EPOLLOUT: 可写事件\n"); printf(" EPOLLPRI: 紧急数据可读\n"); printf(" EPOLLERR: 错误条件\n"); printf(" EPOLLHUP: 挂起事件\n"); printf(" EPOLLET: 边缘触发模式\n"); printf(" EPOLLONESHOT: 一次性事件\n"); printf(" EPOLLRDHUP: 对端关闭连接\n\n"); // 示例6: epoll_data_t联合体说明 printf("epoll_data_t联合体(可存储不同类型的数据):\n"); printf(" void *ptr; // 指针\n"); printf(" int fd; // 文件描述符\n"); printf(" uint32_t u32; // 32位无符号整数\n"); printf(" uint64_t u64; // 64位无符号整数\n\n"); // 演示使用用户数据 printf("示例6: 使用用户数据\n"); struct epoll_event event_with_data; event_with_data.events = EPOLLIN; event_with_data.data.fd = sockfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event_with_data) == 0) { printf("使用文件描述符作为用户数据: %d\n", event_with_data.data.fd); // 删除文件描述符 epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL); } // 示例7: 实际应用场景 printf("\n示例7: 实际应用场景\n"); printf("服务器程序中的典型使用流程:\n"); printf("1. 创建epoll实例\n"); printf("2. 添加监听socket到epoll\n"); printf("3. 在事件循环中:\n"); printf(" a. 调用epoll_wait等待事件\n"); printf(" b. 处理就绪事件\n"); printf(" c. 根据需要添加/修改/删除监视的文件描述符\n\n"); printf("常见操作模式:\n"); printf("监听socket: EPOLLIN | EPOLLET\n"); printf("客户端socket: EPOLLIN | EPOLLOUT | EPOLLET\n"); printf("一次性事件: EPOLLIN | EPOLLONESHOT\n\n"); // 清理资源 close(sockfd); close(epfd); printf("总结:\n"); printf("epoll_ctl是管理epoll监视集合的核心函数\n"); printf("支持添加、修改、删除三种基本操作\n"); printf("正确使用事件类型和用户数据很重要\n"); printf("需要妥善处理各种错误情况\n"); return 0; }
|