Linux网络编程---I/O复用模型之poll

Linux网络编程—I/O复用模型之poll

1.函数poll

poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//返回值:若成功,返回就绪的描述符个数,若超时则为0,若出错则为-1
  • fds是指向一个pollfd结构第一个元素的指针。每一个元素都是pollfd结构类型,用于测试某个给定描述符fd的条件
struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

要测试的条件在events中指定,函数相应的revents成员返回该描述符的状态,一个为调用值,一个为返回结果,这与select的传入传出参数不同。
events和revents可以设置以下常量的一个或多个的按位或结果:

POLLIN      //普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
POLLRDNORM  //数据可读
POLLRDBAND  //优先级带数据可读
POLLPRI     //高优先级可读数据
POLLOUT     //普通或带外数据可写
POLLWRNORM  //数据可写
POLLWRBAND  //优先级带数据可写
//下面三个revents不包含
POLLERR     //发生错误
POLLHUP     //发生挂起
POLLNVAL    //描述字不是一个打开的文件
  • timeout指定poll函数返回前等待多长时间,它是一个指定应等待毫秒数的正值。

|timeout值|说明|
|:-:|:-:|
|-1|永远等待|
|0|立即返回,不阻塞进程|
|>0|等待指定数目的毫秒数|

  • nfds 是监控数组中有多少文件描述符需要被监控

如果我们不关心某个特定的描述符,那么可以把与它对应的pollfd结构的fd成员设置为一个负值。poll函数会忽略这样的pollfd结构的events成员,返回时将其revents成员的值置为0。

select模型最多可以创建一个1024个文件描述符大小的集合,这是因为受到fd_set的固定数据类型的限制,而poll则没有这样的问题,因为分配一个pollfd结构的数组并把数组中的元素通知内核称为调用者的责任。但是poll模型任然使用的是轮询模型,效率比较低下。

2. poll模型实现

2.1 服务器端

#include "wrap.h"
#include <poll.h>
#include <limits.h>
#include <sys/stropts.h>

#define MAXLINE         1024
#define INFTIM          -1
#define OPEN_MAX        3000

int main(int argc, char *argv[])
{
        int i, maxi, listenfd, connfd, sockfd;
        int nready;
        ssize_t n;
        char buf[MAXLINE];
        socklen_t clilen;
        struct pollfd client[OPEN_MAX];
        struct sockaddr_in clientaddr;

        listenfd = start_ser(argv[1], argv[2]);//监听文件描述符

        client[0].fd = listenfd;//将监听文件描述符加入pollfd数组中
        client[0].events = POLLIN;//设置为输入可读
        for(i = 1; i < OPEN_MAX; i++){//初始化结构体数组
                client[i].fd = -1;
        }
        maxi = 0;

        while(1){
                nready = poll(client, maxi+1, INFTIM);  //阻塞等待注册时间发生

                if(client[0].revents & POLLIN){//当监听文件描述符响应有新连接请求
                        clilen = sizeof(clientaddr);
                        connfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);//接受新客户

                        for(i = 1; i < OPEN_MAX; i++){
                                if(client[i].fd < 0){
                                        client[i].fd = connfd;  //把新客户的文件描述符加入数组
                                        break;
                                }
                        }

                        if(i == OPEN_MAX){      //连接新客户上限
                                Close(connfd);
                                perr_exit("too many clients");
                        }

                        client[i].events = POLLIN;      //新客户端的处理事件
                        if(i > maxi){
                                maxi = i;       //更新最大值
                        }
                        if(--nready == 0){      //没有可读的描述符跳过本次循环
                                continue;
                        }
                }

                for(i = 1; i <= maxi; i++){     //请求所有客户端的数据
                        if((sockfd = client[i].fd) < 0){
                                continue;
                        }
                        if(client[i].revents & POLLIN){ //POLLIN事件发生
                                memset(buf, '\0', MAXLINE);
                                if((n = Read(sockfd, buf, MAXLINE-1)) < 0){
                                        if(errno == ECONNRESET){        //当客户端发送reset
                                                Close(sockfd);
                                                client[i].fd = -1;
                                        }else{
                                                perr_exit("read err");
                                        }
                                }else if(n == 0){
                                        Close(sockfd);
                                        client[i].fd = -1;
                                }else{
                                        printf("client : %s\n", buf);
                                }

                                if(--nready == 0){      //没有可读的描述符跳过本次循环
                                        break;
                                }
                        }
                }
        }

        Close(connfd);
        Close(listenfd);

        return 0;
}

2.2客户端

#include "wrap.h"

int main(int argc, char *argv[])
{
        struct sockaddr_in serveraddr;
        int connfd;

        connfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(atoi(argv[2]));
        inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);

        Connect(connfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

        char buf[1024];
        while((fgets(buf, 1024, stdin)) != NULL){
                Write(connfd, buf, strlen(buf));
        }

        Close(connfd);
        return 0;
}

2.3程序测试结果

这里写图片描述

这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值