POLL

poll

poll是Linux中的字符设备驱动中的一个函数。Linux 2.5.44版本后,poll被epoll取代。和select实现的功能差不多,poll的作用是把当前的文件指针挂到等待队列。
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
  • 参数说明:
    • fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
    • nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
    • timeout:是poll函数调用阻塞的时间,单位:毫秒;
  • 返回值:
    • 大于0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
    • 等于0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout等于0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
    • -1: poll函数调用失败,同时会自动设置全局变量errno;
struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};
  • 与select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。
  • 如果没有事件发生,revents会被清空,所以你不必多此一举。
  • events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。
  • 如果fd小于0, 则events字段被忽略,而revents被置为0.

    The field fd contains a file descriptor for an open file. If thisfield
    is negative, then the corresponding events field is ignored and the revents field
    returns zero. (This provides an easy way of ignoring a file descriptor for a
    single poll() call: simply negate the fdfield. Note, however, that this technique
    can’t be used to ignore file descriptor 0.)

  • 标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。

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

--
POLLIN有数据可读。
POLLRDNORM有普通数据可读。
POLLRDBAND有优先数据可读。
POLLPRI有紧迫数据可读。
POLLOUT写数据不会导致阻塞。
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被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。

timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。

返回值和错误代码

成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
EBADF 一个或多个结构体中指定的文件描述符无效。

EFAULTfds 指针指向的地址超出进程的地址空间。

EINTR 请求的事件之前产生一个信号,调用可以重新发起。

EINVALnfds参数超出PLIMIT_NOFILE值。

ENOMEM 可用内存不足,无法完成请求。

代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <poll.h>
#include <limits.h>     /*for OPEN_MAX*/
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>


#ifndef OPEN_MAX
#define OPEN_MAX 1024
#endif

#ifndef INFTIM
#define INFTIM -1
#endif

#define PORT 8888
#define MAX_LINE 2048
#define LISTENQ 20


int main(int argc , char **argv)
{
    int i, maxi, listenfd, connfd, sockfd;

    int nready;

    ssize_t n, ret;

    struct pollfd client[OPEN_MAX];

    char buf[MAX_LINE];

    socklen_t clilen;

    struct sockaddr_in servaddr , cliaddr;

    /*(1) 得到监听描述符*/
    listenfd = socket(AF_INET , SOCK_STREAM , 0);

    /*(2) 绑定套接字*/
    bzero(&servaddr , sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    bind(listenfd , (struct sockaddr *)&servaddr , sizeof(servaddr));

    /*(3) 监听*/
    listen(listenfd , LISTENQ);

    /*(4) 设置poll*/
    client[0].fd = listenfd;
    client[0].events = POLLRDNORM;
    for(i=1 ; i<OPEN_MAX ; ++i)
    {
        client[i].fd = -1;
    }//for
    maxi = 0;

    /*(5) 进入服务器接收请求死循环*/
    while(1)
    {
        nready = poll(client , maxi+1 , INFTIM);

        if(client[0].revents & POLLRDNORM)
        {
            /*接收客户端的请求*/
            clilen = sizeof(cliaddr);

            printf("\naccpet connection~\n");

            if((connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen)) < 0)
            {
                perror("accept error.\n");
                exit(1);
            }//if       

            printf("accpet a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr) , cliaddr.sin_port);

            /*将客户链接套接字描述符添加到数组*/
            for(i=1 ; i<OPEN_MAX ; ++i)
            {
                if(client[i].fd < 0)
                {
                    client[i].fd = connfd;
                    break;
                }//if
            }//for

            if(OPEN_MAX == i)
            {
                perror("too many connection.\n");
                exit(1);
            }//if

            /*该描述符等待的事件*/
            client[i].events = POLLRDNORM;
            if(i > maxi)
                maxi = i;

            if(--nready < 0)
                continue;
        }//if

        for(i=1; i<=maxi ; ++i)
        {
            if((sockfd = client[i].fd) < 0)
                continue;
            /*该链接描述符实际发生的事件*/
            if(client[i].revents & (POLLRDNORM | POLLERR))
            {
                /*处理客户请求*/
                printf("\nreading the socket~~~ \n");

                bzero(buf , MAX_LINE);
                if((n = read(sockfd , buf , MAX_LINE)) <= 0)
                {
                    close(sockfd);              
                    client[i].fd = -1;
                }//if
                else{
                    printf("clint[%d] send message: %s\n", i , buf);
                    if((ret = write(sockfd , buf , n)) != n)    
                    {
                        printf("error writing to the sockfd!\n");
                        break;
                    }//if
                }//else
                if(--nready <= 0)
                    break;
            }//if
        }//for
    }//while
    exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值