Linux网络基础-高级IO之poll

本文介绍了poll()函数的基本用法及特点,对比了其与select()函数的区别,并通过一个基于poll()实现的ECHO服务器示例代码,展示了如何使用poll()进行网络编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

poll()函数:
#include <poll.h>

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

struct pollfd {
    int   fd;         /* 要监听的文件描述符 */
    short events;     /* 监听的事件集合 */
    short revents;    /* 发生的事件集合 */
};

说明:poll()较select()函数有了进一步的优化,一方面,将监听事件集同返回事件集分开,避免了每次调用重复手动构建监听事件集的麻烦,另一方面,poll()没有监听文件描述符集最大数量的限制,其他功能类似select()函数。
参数:

  • fds:是一个要监听的结构列表,每一个元素包括三个字段,fd:要监听的文件描述符,events:监听的事件集合,revents:发生的事件集合。
  • nfds:fds列表的长度。
  • timeout:poll()等待的时间,单位为ms,值为-1时与select的timeout参数值为NULL时功能相同,select()函数的timout参数。

返回值:

  • 大于0:表示监听的文件描述符列表中,发生事件的文件描述符个数。
  • 等于0:timeout时间内还没有事件发生,超时返回0。
  • 小于-1:调用失败,错误原因存于errno。

备注:events和revents的取值范围如下所示:

事件描述是否可作为输入是否可作为输出
POLLIN数据(包括普通数据和优先数据)可读
POLLRDNORM普通数据可读
POLLPRI高优先级数据可读,如TCP带外数据
POLLOUT数据(包括普通数据和优先数据)可写
POLLWRNORM普通数据可写
POLLRDHUPTCP连接被对方关闭,或者对方关闭了写操作
POLLERR错误
POLLHUP挂起,如管道的写端关闭后,读端将收到POLLHUP事件
POLLNVAL文件描述符没有打开

注:socket的就绪条件同select。

poll()优点:
  • 将监听事件集同返回事件集分开,避免了每次调用重复手动构建监听事件集的麻烦。
  • poll()没有监听文件描述符集最大数量的限制。
poll()缺点:
  • 每次调用都需要把大量的poofd结构从用户拷贝到内核态。
  • 内核在监听文件描述符集的时候需要轮询文件描述符列表,在文件描述符列表很大时,效率会很低。
  • poll返回后,需要遍历pollfd列表获取就绪的文件描述符,在列表很大时,性能会下降。
基于poll()的ECHO服务器:

server.c:

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

void Init(struct pollfd* fdlist,int size){
  int i = 0;
  for(;i < size;i++){
    fdlist[i].fd = -1;
    fdlist[i].events = 0;
    fdlist[i].revents = 0;
  }
}

void Add(int fd,struct pollfd* fdlist,int size){
  int i = 0;
  for(;i < size;i++){
    if(fdlist[i].fd == -1){
      fdlist[i].fd = fd;
      fdlist[i].events = POLLIN;
      break;
    }
  }
}

int main(int argc,char *argv[]){
  if(argc != 3){
    printf("Usage :./server ip port\n");
    return -1;
  }
  int sock = socket(AF_INET,SOCK_STREAM,0);
  if(sock < 0){
    perror("socket");
    return -2;
  }
  struct sockaddr_in saddr;
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = inet_addr(argv[1]);
  saddr.sin_port = htons(atoi(argv[2]));
  int opt = 1;
  setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  int ret = bind(sock,(struct sockaddr*)&saddr,sizeof(saddr));
  if(ret < 0){
    perror("bind");
    return -3;
  }
  ret = listen(sock,5);
  if(ret < 0){
    perror("listen");
    return -4;
  }
  struct pollfd fdlist[1024];
  Init(fdlist,sizeof(fdlist)/sizeof(struct pollfd));
  Add(sock,fdlist,sizeof(fdlist)/sizeof(struct pollfd));
  while(1){
    int ret = poll(fdlist,sizeof(fdlist)/sizeof(struct pollfd),1000);
    if(ret < 0){
      perror("poll");
      continue;
    }
    if(ret == 0){
      printf("poll timeout\n");
      continue;
    }
    int i = 0;
    for(;i < sizeof(fdlist)/sizeof(struct pollfd); i++){
      if(fdlist[i].fd == -1){
        continue;
      }
      if(!(fdlist[i].revents & POLLIN)){
        continue;
      }
      if(fdlist[i].fd == sock){
        struct sockaddr_in caddr;
        socklen_t len = sizeof(caddr);
        int connect_fd = accept(sock,(struct sockaddr*)&caddr,&len);
        if(connect_fd < 0){
          perror("connect");
          continue;
        }
        Add(connect_fd,fdlist,sizeof(fdlist)/sizeof(struct pollfd));
      }else{
        char buf[1024] = {0};
        ssize_t readsize = read(fdlist[i].fd,buf,sizeof(buf)-1);
        if(readsize < 0){
          perror("read");
          continue;
        }
        if(readsize == 0){
          printf("client say goodbay\n");
          close(fdlist[i].fd);
          fdlist[i].fd = -1;
          fdlist[i].events = 0;
          fdlist[i].revents = 0;
          continue;
        }
        printf("client $:%s",buf);
        write(fdlist[i].fd,buf,strlen(buf));
      }
    }
  }
  return 0;
}

client.c:

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


int main(int argc,char *argv[]){
  if(argc != 3){ 
    perror("Usage :./server ip port\n");
    return -1;
  }
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr(argv[1]);
  addr.sin_port = htons(atoi(argv[2]));
  int sock = socket(AF_INET,SOCK_STREAM,0);
  if(sock < 0){
    perror("socket");
    return -1;
  }
  int ret = connect(sock,(struct sockaddr*)&addr,sizeof(addr));
  if(ret < 0){
    perror("connect");
    return -2;
  }
  while(1){
    char buf[1024] = {0};
    printf("> ");
    fflush(stdout);
    read(0,buf,sizeof(buf)-1);
    ssize_t writesize = write(sock,buf,strlen(buf));
    if(writesize < 0){
      perror("write");
      continue;
    }
    ssize_t readsize = read(sock,buf,sizeof(buf)-1);
    if(readsize < 0){
      perror("read");
      continue;
    }
    if(readsize == 0){
      printf("server close\n");
      break;
    }
    buf[readsize] = 0;
    printf("server say:%s",buf);
  }
  close(sock);
  return 0;
}

结果展示:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值