poll是Linux中的字符设备驱动中的一个函数。和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列。
参数说明:
fd是一个poll函数监听的结构列表。每一个元素中,包含了三部分内容:文件描述符、监听的事件集合、返回的事件集合;
nfds表示fds数组的长度;
timeout表示poll函数的超时时间,单位是毫秒(ms)。
events和revents的取值:
返回结果:
(1)返回值小于0,表示出错;
(2)返回值等于0,表示poll函数等待超时;
(3)返回值大于0,表示poll由于监听的文件描述符就绪而返回。
- poll的优点
不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现。
poll结构包含了要监视的event和要发生的event,不再使用select“参数——值”传递的方式,接口使用比select更方便。
poll并没有最大数量限制,但是数量过大后性能还是会下降。
- poll的缺点
poll中监听的文件描述符数目增多时:
(1)和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符;
(2)每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核态;
(3)同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符的数量的增长,其效率也会线性下降。
- 多路转接服务器
#include <stdio.h>
#include<unistd.h>
#include<poll.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
typedef struct pollfd pollfd;
void Init(pollfd* fd_list,int size){
int i = 0;
for(i = 0;i < size;i++){
fd_list[i].fd = -1;
fd_list[i].events = 0;
fd_list[i].revents = 0;
}
}
void Add(int fd,pollfd* fd_list,int size){
int i = 0;
for(i = 0;i < size;i++){
if(fd_list[i].fd == -1){
fd_list[i].fd = fd;
fd_list[i].events = POLLIN;
break;
}
}
}
int main(int argc,char* argv[]){
if(argc != 3){
printf("Usage ./server [ip] [port]\n");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
int listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0){
perror("socket");
return 2;
}
int ret = bind(listen_fd,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0){
perror("bind");
return 3;
}
ret = listen(listen_fd,5);
if(ret < 0){
perror("listen");
return 4;
}
pollfd fd_list[1024];
Init(fd_list,sizeof(fd_list)/sizeof(pollfd));
Add(listen_fd,fd_list,sizeof(fd_list)/sizeof(pollfd));
for(;;){
int ret = poll(fd_list,sizeof(fd_list)/sizeof(pollfd),1000);
if(ret < 0){
perror("poll");
continue;
}
if(ret == 0){
printf("poll timeout\n");
continue;
}
size_t i = 0;
for(i = 0;i < sizeof(fd_list)/sizeof(pollfd);i++){
if(fd_list[i].fd == -1)
continue;
if(! (fd_list[i].revents & POLLIN))
continue;
if(fd_list[i].fd == listen_fd){
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int connect_fd = accept(listen_fd,(struct sockaddr*)&client_addr,&len);
if(connect_fd < 0){
perror("accept");
continue;
}
Add(connect_fd,fd_list,sizeof(fd_list)/sizeof(pollfd));
}
else{
char buf[1024] = {0};
ssize_t read_size = read(fd_list[i].fd,buf,sizeof(buf)-1);
if(read_size < 0){
perror("read");
continue;
}
if(read_size == 0){
printf("client say:stop...\n");
close(fd_list[i].fd);
fd_list[i].fd = -1;
}
printf("client say:%s\n",buf);
write(fd_list[i].fd,buf,strlen(buf));
}
}
}
return 0;
}
客户端:
#include <stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#define MAX 128
int main(int argc,char* argv[]){
if(argc != 3){
printf("Usage:%s [ip] [port]\n",argv[0]);
return 1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
printf("socket error!\n");
return 2;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0){
printf("connect error!\n");
return 3;
}
char buf[MAX];
while(1){
printf("please Enter# ");
fflush(stdout);
read(0,buf,sizeof(buf)-1);
/*if(s > 0){
buf[s-1] = 0;
if(strcmp("quit",buf) == 0){
printf("client quit!\n");
break;
}*/
ssize_t write_size = write(sock,buf,strlen(buf));
if(write_size < 0){
perror("write");
continue;
}
ssize_t s = read(sock,buf,sizeof(buf)-1);
if(s < 0){
perror("read");
continue;
}
if(s == 0){
printf("server close!\n");
break;
}
//buf[s] = 0;
printf("server Echo# %s\n",buf);
}
close(sock);
return 0;
}