poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll和select效率差不多,只是其使用接口相对简单些,poll不在局限于1024个文件描述符,poll监听事件和触发事件分开,event表示监听事件,revents表示触发的事件。相比select不用每一次都需要重新设置监听事件。
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); //第一个参数是struct pollfd数组 struct pollfd { int fd; /* file descriptor */你要监控文件描述符 short events; /* requested events */ 监听文件描述符上的事件 传入参数由用户设置 short revents; /* returned events */监控文件描述符事件返回值 传出参数由内核设置 }; POLLIN普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND POLLRDNORM-数据可读 POLLRDBAND-优先级带数据可读 POLLPRI 高优先级可读数据 POLLOUT普通或带外数据可写 POLLWRNORM-数据可写 POLLWRBAND-优先级带数据可写 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件 第二个参数,指结构体数组长度。 timeout 毫秒级等待 -1:阻塞等,#define INFTIM -1 Linux中没有定义此宏 0:立即返回,不阻塞进程 >0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值
poll server端实例:
#include<stdio.h> #include<string.h> #include<poll.h> #include <sys/un.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include<errno.h> #define OPEN_MAX 1024 int create_listen(int port) { int listen_st,on; struct sockaddr_in s_addr; listen_st =socket(AF_INET,SOCK_STREAM,0); if(listen_st==-1) { perror("socket error "); return -1; } if(setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1) { perror("setsockopt error"); return -1; } s_addr.sin_port=htons(port); s_addr.sin_family=AF_INET; s_addr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(listen_st,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1) { perror("bind error"); return -1; } if (listen(listen_st, 5) == -1) // 设置文件描述符具有监听的功能 { perror("listen error"); return -1; } return listen_st; } int run_server(int port) { int i,maxi,listen_st,conn_st,sockaddr_len; int nready; struct pollfd client[OPEN_MAX]; char buf[1024]; struct sockaddr_in c_addr; listen_st=create_listen(port); if(listen_st==-1) { return -1; } for(i=1;i<OPEN_MAX;i++) { client[i].fd=-1; } client[0].fd=listen_st; client[0].events=POLLIN; maxi=0; while(1) { nready = poll(client,maxi+1,-1);//poll 阻塞 if(nready<0) { perror("poll error"); break; } if((client[0].revents&POLLIN))//检测listen_st { sockaddr_len=sizeof(c_addr); conn_st=accept(listen_st,(struct sockaddr *)&c_addr,&sockaddr_len); printf("received form %s at port:%d \n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port)); for(i=0;i<OPEN_MAX;i++) { if(client[i].fd<0) { client[i].fd=conn_st; client[i].events=POLLIN; break; } } if(i==OPEN_MAX) { printf("too many client \n"); close(conn_st); }else { if(i>maxi) //记录最大下标 { maxi=i; } } if(--nready==0) continue; } for(i=1;i<=maxi;i++) { if((conn_st=client[i].fd)<0) { continue; } if(client[i].revents&POLLIN) { memset(buf,0,sizeof(buf)); int rv=read(conn_st,buf,sizeof(buf)); if(rv<0) { if(errno==ECONNRESET)/* 当收到RST标志时*/ //这种错误是由于客户端发过FIN ACk掉线了客服端进程已经结束了 服务端再发FIN 客户端会发送RST { printf("client aborted connection \n"); close(conn_st); client[i].fd=-1; } } else if(rv==0) { printf("close client \n"); close(conn_st); client[i].fd=-1; } else { printf("recv from client:%s \n",buf); write(conn_st,buf,strlen(buf)); } if (--nready == 0) break; //就绪个数减一 } } } close(listen_st); return 0; } int main(int argc,char *argv[]) { if(argc<2) { printf("usage:%s port \n",argv[0]); return 0; } int port=atoi(argv[1]); if(port==0) { printf("port error \n"); return 0; } printf("start server \n"); run_server(port); return 0; }
版权声明:欢迎转载,如有不足之处,恳请斧正。
时间: 2024-07-29 15:24:23