IO多路复用之poll

poll和select区别:
① poll服务器监视的文件描述符无上限;
② poll将输入、输出参数进行分离。

一、poll函数
函数格式如下所示:

#include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现,即poll将输入、输出参数进行分离。

struct pollfd * fds
pollfd 结构体定义如下:

struct pollfd {
    int fd;               /* 文件描述符 */
    short events;         /* 等待的事件 */
    short revents;        /* 实际发生了的事件 */
} ;

  每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。
  每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。
  revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。
  events域中请求的任何事件都可能在revents域中返回。
  合法的事件如下:

    POLLIN        有数据可读。
   POLLRDNORM      有普通数据可读。
   POLLRDBAND     有优先数据可读。
   POLLPRI       有紧迫数据可读。
   POLLOUT       写数据不会导致阻塞。
   POLLWRNORM     写普通数据不会导致阻塞。
   POLLWRBAND     写优先数据不会导致阻塞。
   POLLMSGSIGPOLL   消息可用

  此外,revents域中还可能返回下列事件:

    POLLER    指定的文件描述符发生错误。

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

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

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

  使用poll()和select()不一样,你不需要显式地请求异常情况报告。
  POLLIN | POLLPRI等价于select()的读事件.
  POLLOUT |POLLWRBAND等价于select()的写事件。
  POLLIN等价于POLLRDNORM |POLLRDBAND
  而POLLOUT则等价于POLLWRNORM。

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

unsigned int nfds,

  nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量
int timeout

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

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

    EBADF         一个或多个结构体中指定的文件描述符无效。

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

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

   EINVALnfds  参数超出PLIMIT_NOFILE值。

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

二、代码演示(服务器只读演示)

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<string.h>
#include<poll.h>

#define SIZE 100

static void usage(const char* proc)
{
    printf("usage:%s [local_ip] [local_port]\n",proc);
}

int startup(char* ip, int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock<0)
    {
        perror("sock");
        exit(1);
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = ntohs(port);
    local.sin_addr.s_addr = inet_addr(ip);

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

    if(listen(sock, 10) <0)
    {
        perror("listen");
        exit(3);
    }

    return sock;
}

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

    int listen_sock = startup(argv[1], atoi(argv[2]));

    struct pollfd evs[SIZE];
    int i = 0;
    for(; i<SIZE; i++)
    {
        evs[i].fd = -1;
        evs[i].events = -1;
        evs[i].revents = -1;
    }

    evs[0].fd = listen_sock;
    evs[0].events = POLLIN;

    int timeout = 3000;

    while(1)
    {
        switch(poll(evs, SIZE, timeout))
        {
        case 0:
           // printf("timeout\n");
            break;

        case -1:
            perror("poll");
            break;

        default:
            {
                for(i=0; i<SIZE; i++)
                {
                   if(i==0 && evs[i].revents == POLLIN)
                   {
                       struct sockaddr_in client;
                       socklen_t len = sizeof(client);
                       int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);
                       if(new_sock < 0)
                       {
                           perror("accept");
                           continue;
                       }
                       printf("get a client:%s %d\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port));

                       int j = 1;
                       for(; j<SIZE; j++)
                       {
                            if(evs[j].fd == -1)
                            {
                                evs[j].fd = new_sock;
                                evs[j].events = POLLIN;
                                break;
                            }
                       }

                       if(j == SIZE)
                       {
                            close(new_sock);
                            continue;
                       }
                   }
                   else if(i != 0 && evs[i].revents == POLLIN)
                   {
                       char buf[1024];
                       ssize_t ss = read(evs[i].fd, buf, sizeof(buf)-1);

                       if(ss > 0)
                       {
                           buf[ss] = 0;
                           printf("client# %s\n", buf);
                       }
                       else if(ss == 0)
                       {
                           printf("client is quit!\n");
                           close(evs[i].fd);
                           evs[i].fd = -1;
                           break;
                       }
                       else
                       {
                           perror("read");
                           close(evs[i].fd);
                           evs[i].fd = -1;
                           continue;
                       }
                   }
                   else// write events
                   {
                   }
                }
            }
        }
    }
    return 0;
}

poll服务器启动:
这里写图片描述

用telnet链接测试:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fly_bit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值