IO多路复用之poll-创新互联

poll提供的功能与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,但poll比select的优点是,不限制所能监视的描述符的数目,但随着所监视描述符的数目的增加,性能也会下降

成都创新互联公司制作网站网页找三站合一网站制作公司,专注于网页设计,网站制作、成都网站建设,网站设计,企业网站搭建,网站开发,建网站业务,680元做网站,已为成百上千服务,成都创新互联公司网站建设将一如既往的为我们的客户提供最优质的网站建设、网络营销推广服务!

函数原型:

#include

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

返回值:成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,返回0;失败返回-1

参数:

fds:结构体指针,该结构体结构如下:

        struct pollfd{

              int fd;        //所感兴趣的文件描述符

              short events;   //用于指定等待的事件

              short revents;  //用于指定poll返回时,在该文件描述符上实际发生了的事件

          };

每一个pollfd结构体制定了一个被监视文件描述符,可传递多个该结构体,指示poll监视多个文件描述符

nfds:要监视的描述符的个数

timeout:单位(微秒),timeout指定等待的毫秒数,无论I/O是否准备好,poll都会返回,指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件,立即返回

events域中请求的任何事件都可能在revents域中返回。合法的事件如下:

   POLLIN         有数据可读

   POLLPRI        有紧迫数据可读

POLLOUT         写数据不会导致阻塞

POLLRDNORM       有普通数据可读。

POLLRDBAND      有优先数据可读。

POLLWRNORM     写普通数据不会导致阻塞。

POLLWRBAND      写优先数据不会导致阻塞。

POLLMSGSIGPOLL    消息可用。

此外,revents域中还可能返回下列事件:
POLLER        指定的文件描述符发生错误。

POLLHUP       指定的文件描述符挂起事件。

POLLNVAL      指定的文件描述符非法。

    这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。

POLLIN | POLLPRI等价于select()的读事件,POLLOUT |POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM |POLLRDBAND,而POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。

示例代码如下:

编写一个echo server程序,功能是客户端向服务器发送信息,服务器接收输出并原样发送回给客户端,客户端接收到输出到终端

server_poll.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define _BLOCKLOG_ 6

void usage(char *_proc)
{
    printf("%s [ip] [port]\n",_proc);
}

int create(char *_ip,int _port)
{
    int listen_fd=socket(AF_INET,SOCK_STREAM,0);
    if(listen_fd<0){
        perror("socket");
        exit(1);
    }   
    
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(_ip);

    struct linger lig;
    int iLen;
    lig.l_onoff=1;
    lig.l_linger=0;
    iLen=sizeof(struct linger);
    setsockopt(listen_fd,SOL_SOCKET,SO_LINGER,(char *)&lig,iLen);

    if(bind(listen_fd,(struct sockaddr*)&local,sizeof(local))<0){
        perror("bind");
        exit(2);
    }

    if(listen(listen_fd,_BLOCKLOG_)<0){
        perror("listen");
        exit(3);
    }

    return listen_fd;
}

int main(int args,char *argv[])
{
    if(args!=3){
        usage(argv[0]);
        return 1;
    }   

    char *ip=argv[1];
    int port=atoi(argv[2]);
    //创建监听描述符并绑定
    int listen_fd=create(ip,port);

    nfds_t nfds=64;
    struct pollfd fds[nfds];

    //初始化描述符
    int i=0;
    for(;i0 && (fds[i].revents&POLLIN)){//normal events happend
                                //接收客户端发来的消息
                            ssize_t _size=read(fds[i].fd,buf,sizeof(buf)-1);
                            if(_size<0){
                                perror("read");
                            }else if(_size==0){//client closed
                                printf("client shutdown\n");
                                close(fds[i].fd);
                                fds[i].fd=-1;
                                continue;
                            }else{
                                buf[_size]='\0';
                                printf("client# %s",buf);
                                //向客户端发送消息
                                if(write(fds[i].fd,buf,sizeof(buf)-1)<0){
                                    perror("write");
                                }
                            }
                        }
                    }
                    break;
                }
                break;
        }
    }

    return 0;
}

client_poll.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void usage(char *proc)
{
    printf("%s [ip] [port]\n",proc);
}

int creat_socket()
{
    int fd=socket(AF_INET,SOCK_STREAM,0);
    if(fd<0){
        perror("socket");
        exit(1);
    }

    return fd;
}

int main(int argc,char* argv[])
{
    if(argc!=3){
        usage(argv[0]);
        exit(1);
    }

    int fd=creat_socket();

    int _port=atoi(argv[2]);
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(_port);
    inet_aton(argv[1],&addr.sin_addr);
    socklen_t addrlen=sizeof(addr);
    if(connect(fd,(struct sockaddr*)&addr,addrlen)<0){
        perror("connect");
        exit(2);
    }

    char buf[1024];
    while(1){
        memset(buf,'\0',sizeof(buf));
        printf("Please Enter:");
        fgets(buf,sizeof(buf)-1,stdin);
        if(send(fd,buf,sizeof(buf)-1,0)<0){
            perror("send");
            continue;
        }

        ssize_t _size=recv(fd,buf,sizeof(buf)-1,0);
        if(_size>0){
            buf[_size]='\0';
            printf("echo->%s",buf);
        }
  }
  return 0;
}

运行结果:

服务器端运行结果:

IO多路复用之poll

客户端1运行结果:

IO多路复用之poll

客户端2运行结果:

IO多路复用之poll

最后还有一点,poll()会自动把revents域设置为0,不需要我们手动的去设置

测试程序如下:

以下程序片段只是在上面代码的基础上添加了几行代码,为看的更清楚,我用红色注释了这几行代码

IO多路复用之poll

运行出来的结果如下:

IO多路复用之poll

现在对上面运行结果进行分析,为简单起见,我们只关注四个结构体,所以以四行为单位

可看到第一次(前四行)的revents都为随机值

第二次(次四行)poll()函数把fd=3的描述符中revents设置为0

第四次poll()函数把fd=3的描述符中revents设置为1(因为此时监听到了客户端请求)所以接下来有:

get a connection...1.0.0.0:0  这条消息

第五次fd=3的revents还是为1,因为此时还没有执行poll(),fd=4的revents是随机值

第六次fd=3和fd=4的events都为1,它们都添加了读事件,而fd=3的revents被poll设置为了0,fd=4的revents为1(因为客户端发送了条消息,使读事件发生,因此有下面一条消息:)

client# hello

第七次和第六次的一致,因为还未被poll()设置

第八次和第七次的一致,但此时fd=4的revents=1是因为客户端按下了Ctrl+c,所以接下来会有:

client shutdown

第九次把fd=4从数组中去掉

《完》

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


标题名称:IO多路复用之poll-创新互联
本文地址:http://pwwzsj.com/article/dpdgso.html