select
一、select
#include <sys/select.h> void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set); int select(int nfds, fd_set *readfds, fd_set * writefds, fd_set *exceptfds, struct timeval *timeout); int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask); struct timeval{ long tv_sec; long tv_usec; };` struct timespec { long tv_sec; long tv_nsec; };
同时监听多个阻塞的文件描述符(如多个网络连接),任一文件有数据就返回。
nfds;所关心的最大fd+1.
readfs:传入:关心的可读文件;传出:实际可读文件。
timeout:1)NULL死等;2)tv_sec=0,tv_usec=0,立即返回,0s,不等;3)tv_sec=n,tv_usec=m;有限等。
失败:-1;无设备可用:0;有设备可用,返回正数,所有可读fd数目之和。
注: Some code calls select() with all three sets empty, nfds zero, and a non-NULL timeout as a fairly portable way to sleep with subsecond precision.
可读:1)有数据;2)关闭连接;3)有新的连接请求(监听socketfd)。
可写:1)无流量控制;2)关闭连接。
异常:1)收到紧急数据。
注:普通文件描述符总是会立即或多或少地立即返回结果或文件结束,select()会一直上报直到文件结束(读)或一直写,因此select()操作无意义
。epoll_wait()对普通文件直接无感知,不会上报普通文件或目录的读写事件。
代码示例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAXLINE 80 #define SERV_PORT 2000 int main(void) { int i, maxi, maxfd, listenfd, connfd, sockfd, ret; int nready, client[FD_SETSIZE]; ssize_t n; fd_set rset, allset; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; socklen_t cliaddr_len; struct sockaddr_in cliaddr, servaddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd < 0) { perror("listen"); return -1; } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); if(ret < 0) { perror("bind"); return -1; } listen(listenfd, 10); maxfd = listenfd; maxi = -1; for(i = 0; i < FD_SETSIZE; i++) client[i] = -1; FD_ZERO(&allset); FD_SET(listenfd, &allset); while(1){ rset = allset; nready = select(maxfd+1, &rset, NULL, NULL, NULL); if(nready < 0){ perror("select"); exit(EXIT_FAILURE); } if(FD_ISSET(listenfd, &rset)) { cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); if(connfd < 0){ perror("accept"); exit(EXIT_FAILURE); } printf("received from %s at port %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); for(i = 0; i < FD_SETSIZE; i++) { if(client[i] < 0) { client[i] = connfd; break; } } if(i == FD_SETSIZE) { //efputs("too many clients.\n"); printf("too many clients.\n"); exit(EXIT_FAILURE); } FD_SET(connfd, &allset); if(connfd >maxfd) { maxfd = connfd; } if(i > maxi) maxi = i; if(--nready == 0) continue; } for(i = 0; i<= maxi; i++) { if((sockfd = client[i]) < 0) continue; if(FD_ISSET(sockfd, &rset)) { if((n = read(sockfd, buf, MAXLINE)) == 0) { close(sockfd); FD_CLR(sockfd, &allset); client[i] = -1; } else { int j; for(j = 0; j < n; j++) buf[j] = toupper(buf[j]); write(sockfd, buf, n); } if(--nready == 0) break; } } } }
二、select&epoll
select将进程挂入多个设备的阻塞队列中,扫描设备(相当长时间),监听数据。 O(n)
- 节省空间
- 通用性好(posix)
- 唤醒后自动扫描事件
- 文件:任意文件
epoll提取设备,封装为event。 O(1)
- 消耗空间
- 仅在linux下使用
- 唤醒后自动得到事件队列
- 不能为普通文件
注:普通文件描述符总是会立即或多或少地立即返回结果或文件结束,select()会一直上报直到文件结束(读)或一直写,因此select()操作无意义
。
epoll_wait()对普通文件直接无感知,不会上报普通文件或目录的读写事件。
参考:
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· 几个技巧,教你去除文章的 AI 味!
· 对象命名为何需要避免'-er'和'-or'后缀
· 系统高可用的 10 条军规
· 关于普通程序员该如何参与AI学习的三个建议以及自己的实践
· AI与.NET技术实操系列(八):使用Catalyst进行自然语言处理