I/O多路转接之poll

已经有select实现IO多路转接了,为什么还要有poll?
之前说过,select有很多缺点:
这里写图片描述
而poll的出现解决了select的:
1.可监控的文件描述符个数有上限;
2.参数即作输入又作输出两个问题。

poll函数声明:
这里写图片描述
参数解释:
①fds:监听结构列表
struct polled结构:
这里写图片描述

events与revents的取值,我仅介绍可读和可写:
数据可读:POLLIN
数据可写:POLLOUT

②nfds:数组的长度
③timeout:等待时间,单位是毫秒
这里写图片描述

所以poll的使用,一般是先定义一个struct polled类型的数组,假设要监视0号文件描述符的读事件,就把数组的第一个元素的fd设为0,events设为POLLIN,而revents为返回,当数组的第一个元素的revents变为POLLIN,表示0号文件描述符的读事件就绪。

返回值解释:

大于0:监听的文件描述符就绪,poll返回
等于0:超时返回
小于0:出错

编写poll代码:
一:检测标准输入

int main()
{
    //因为只关心0一个fd,所以就不定义数组了,第二个参数(数组的大小)填1就好

    //关心0(标准输入)的读事件
    struct pollfd poll_fd;
    poll_fd.fd = 0;//标准输入
    poll_fd.events = POLLIN;//读事件
    //poll_fd.revents为输出型,返回事件的集合

    while(1){
        int ret = poll(&poll_fd,1,3000);//数组大小为1,等待时间为3000ms(3秒)
        if(ret < 0){
            perror("poll");
            continue;
        }
        if(ret == 0){
            printf("超时!\n");
            continue;
        }
        if(poll_fd.revents == POLLIN){//当0的读事件就绪时
            char buf[1024] = {0};
            read(0,buf,sizeof(buf)-1);
            printf("input: %s\n",buf);
        }
    }

    return 0;
}

运行效果为,如果超过3秒不往标准输入写数据,poll会立刻超时:
这里写图片描述
如果向标准输入写数据,则标准输入的读会就绪,然后把数据回显:
这里写图片描述

二.模拟实现poll服务器

//初始化监控列表数组,把fd设为-1,监听和返回事件设为0
void Initpolled(struct 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;
    }
}

//按顺序往监听列表添加fd为sock,events为POLLIN的元素
void Add(int sock,struct pollfd* fd_list,int size)
{
    int i = 0;
    for(i = 0; i<size; i++){
        if(fd_list[i].fd < 0){//当这个元素的fd为-1时,说明没有被添加
            fd_list[i].fd = sock;
            fd_list[i].events = POLLIN;
            break;
        }
    }
}

int main(int argc,char* argv[])
{
    //建立监听套接字
    if(argc != 3){
        printf("./server [ip] [port]\n");
        return 1;
    }
    int listen_sock = socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock < 0){
        perror("socket");
        return 2;
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr(argv[1]);
    local.sin_port = htons(atoi(argv[2]));

    if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0){
        perror("bind");
        return 3;
    }

    if(listen(listen_sock,5) < 0){
        perror("listen");
        return 4;
    }

    //建一个polled结构的数组
    struct pollfd fd_list[1024];
    //初始化polled
    Initpolled(fd_list,sizeof(fd_list)/sizeof(fd_list[0]));
    //把listen_sock添加进数组的第0个,关心读
    Add(listen_sock,fd_list,sizeof(fd_list)/sizeof(fd_list[0]));

    while(1){
        int ret = poll(fd_list,sizeof(fd_list)/sizeof(fd_list[0]),2000);//等待时间为2秒
        if(ret < 0){
            perror("poll");
            continue;
        }

        if(ret == 0){
            printf("poll超时!\n");
            continue;
        }

        //else,则有fd的就绪
        int i = 0;
        for(i = 0; i<sizeof(fd_list)/sizeof(fd_list[0]); i++){
            if(fd_list[i].fd == -1)//fd为-1则继续往后走
                continue;
            if(!(fd_list[i].revents & POLLIN)){//不是读就绪则继续走
                continue;
            }

            if(fd_list[i].fd == listen_sock){//listen_sock就绪,accept建立新连接
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
                if(new_sock < 0){
                    perror("new_sock");
                    continue;
                }
                //把new_sock按顺序放入数组
                Add(new_sock,fd_list,sizeof(fd_list)/sizeof(fd_list[0]));
            }
            else{//处理new_sock就绪,可以读了
                char buf[1024];
                ssize_t s = read(fd_list[i].fd,buf,sizeof(buf)-1);
                if(s < 0){
                    perror("read");
                    continue;
                }
                printf("client >: $s\n",buf);
            }
        }
    }
    return 0;
}

客户端与select相同。

poll的优点:
1.polled结构包含了监视事件events与返回事件revents,不再和select一样参数即作输入又作输出;
2.poll监听的文件描述符没有最大数量限制。

poll的缺点:
1.poll返回后也需要轮循polled列表来获取就绪的fd;
2.需要把polled从用户态拷贝至内核态,开销很大;
3.虽然没有数量限制,但是数量过大性能也会线性下降。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值