poll服务器

xiaoxiao2021-02-28  98

poll是I/O复用多路转接的另一种方法,其优化了select两个缺点:

poll服务器支持的文件描述符数目没有上限;poll服务器函数接口的参数与select不同,其将输入与输出参数进行了分离(用结构体实现).

函数如下:

#include <poll.h> int poll(struct pollfd* fds,nfds_t nfds,int timeout);

参数含义:

fds:结构体数组,(可自定义数组的大小,以连接客户端的数目,所以以上可说其支持的文件描述符无上限,因为个数大小由用户可自定义大小),每一个结构体内容如下,并且可以看到其将输入输出型参数分离: struct pollfd { int fd; //关心的文件描述符 short events; //其作输入型参数指定fd关心的事件 short revents;//其作输出型参数指定fd发生的哪一个事件就绪 }; nfds:结构体数组的大小;timeout:与select含义相同,不过此处timeout不为结构体,只为一个整数值可直接设定,代表ms,含义如下: NULL:poll则一直阻塞等待事件就绪; 0:则仅检测fd状态后不等待立即返回; 特定值:在指定值时间内没有相应事件就绪则超时返回。

poll服务器代码实现如下: poll_server.c

#include <stdio.h> #include <poll.h> #include <sys/socket.h> #include <sys/types.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define SIZE 64 static void usage(const char* porc) { printf("%s [local_ip] [local_porc]\n",porc); } int startup(char* ip,int port) { int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("sock"); close(sock); exit(2); } int opt=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(port); local.sin_addr.s_addr=inet_addr(ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); close(sock); exit(3); } if(listen(sock,10)<0) { perror("sock"); close(sock); exit(4); } return sock; } int main(int argc,char* argv[]) { if(argc != 3) { usage(argv[0]); return 1; } int listen_sock=startup(argv[1],atoi(argv[2])); //创建监听套接字 struct pollfd fds[SIZE]; //poll数组以此来保存每一个客户端信息 int i=0; for( ;i<SIZE;++i) //初始化 { fds[i].fd=-1; fds[i].events=0; fds[i].revents=0; } fds[0].fd=listen_sock; fds[0].events=POLLIN; fds[0].revents=0; int timeout=500; while(1) { switch(poll(fds,SIZE,timeout)) { case -1: perror("poll"); break; case 0: printf("timeout...\n"); break; default: //成功 { struct sockaddr_in client; socklen_t len=sizeof(client); char buf[1024]; int i=0; for( ;i<SIZE;++i) { if(i==0 && fds[i].revents==POLLIN) //为0则为监听套接字建立连接 { int new_sock=accept(listen_sock,(struct sockaddr*)& client,&len); if(new_sock<0) { perror("accept"); close(new_sock); continue; } printf("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); int j=1; for( ;j<SIZE;++j) { if(fds[j].fd==-1) break; } if(j==SIZE) { close(new_sock); continue; } fds[j].fd=new_sock; fds[j].events=POLLIN; } else if(i!=0 && fds[i].revents==POLLIN) //normal fd ready,不为0则直接读取 { ssize_t s=read(fds[i].fd,buf,sizeof(buf)-1); if(s>0) { buf[s]=0; printf("client# %s\n",buf); fds[i].events=POLLOUT; //读取成功则将此关心事件改为写 } else if(s==0) { printf("client quit!!!\n"); close(fds[i].fd); fds[i].fd=-1; continue; } else { perror("read"); continue; } } else if(i!=0 && fds[i].revents==POLLOUT) { ssize_t s=write(fds[i].fd,buf,strlen(buf)); if(s<0) { perror("write"); continue; } fds[i].events=POLLIN; //写成功则将此关心事件改为读 } } } break; } } return 0; }

从上可知,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket,事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

转载请注明原文地址: https://www.6miu.com/read-43151.html

最新回复(0)