1.select的缺点
①监听文件描述符受限制,最大是1024
②每次都要从0开始遍历文件描述符,效率低,需要自己增加满足条件的文件描述符数组提高效率,但增加了编码难度
但select是对路/O转接中唯一支持跨平台的,win、linux等
2.poll的优点
在select的基础上,自己定义了数组结构,可以将监听事件和返回事件集合分离,拓展了监听上限,可以超出1024的限制,但是poll不能实现跨平台操作
3.poll函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
fds:监听文件描述符的数组,加上*表示对数组取地址
struct polled{
int fd; //待监听的文件描述符
short events; //待监听的文件描述符对应的事件
POLLIN、POLLOUT、POLLERR
short revents; //传入时,给0;如果有对应事件满足的话,返回非0;
}
nfds:监听数组的实际有效监听个数
timeout:超时时长,单位为毫秒
-1:阻塞等待
0:立即返回,不阻塞
>0:等待的时长
返回值:返回满足对应监听事件的文件描述符总个数
3.原理分析
首先,要定义一个struct polled类型的数组,将监听描述符初始化到数组中,调用poll函数后,分别对监听事件和读写事件进行判断,有监听事件满足则将accept()返回的cfd加入数组中,有读写事件满足则完成读写操作
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<poll.h>
#include<ctype.h>
#define SERV_PORT 6666
#define OPEN_MAX 1024
int main()
{
int i, j, n;
int lfd, cfd;
char buf[BUFSIZ], str[INET_ADDRSTRLEN]; //INET_ADDRSTRLEN是16
struct sockaddr_in clie_addr, serv_addr;
socklen_t clie_addr_len;
lfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(serv_addr)); //设置端口复用
bzero(&serv_addr, sizeof(serv_addr)); //定义服务器地址结构
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(lfd, 128);
struct pollfd client[OPEN_MAX]; //创建polled类型的数组
client[0].fd = lfd; //将监听文件描述符放入监听序列
client[0].events = POLLIN;
for(i=1; i<OPEN_MAX; i++){
client[i].fd = -1;
}
int maxfd = 0, ret;
while(1){
ret = poll(client, maxfd+1, -1); //阻塞监听是否有事件满足
if(ret>0){
if(client[0].revents & POLLIN){ //监听到有读事件满足
clie_addr_len = sizeodf(clie_addr);
cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
for(i=1; i<OPEN_MAX; i++){
if(client[i].fd<0){
client[i].fd = cfd;
break;
}
if(i==OPEN_MAX){
exit(0);
}
if(maxfd<i){
maxfd = i;
}
if(--ret==0) //select传出只有lfd,无需后续执行
continue;
}
}
for(i=1; i<=maxfd; i++){
if(client[i].fd<0){
continue; //出错
}
if(client[i].event & POLLIN){
n = read(i, buf, sizeof(buf));
if(n<0){
exit(0);
}
if(n==0){
close(i);
client[i].fd = -1; //监听的该文件描述符结束操作,还原回-1
}
for(j=0; j<n; j++){
buf[j] = toupper(buf[j]);
}
write(i, buf, n);
write(STD_OUT, buf, n);
if(--ret==0) //poll传出只有cfd,无需后续执行
continue;
}
}
}
}
close(lfd);
return 0;
}