Linux C编程(十) 之 I/O复用-----poll机制

poll和select对比
  1. 不同:
    select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。
    pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。
    pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。
  2. 相同:
    和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
    从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
poll函数介绍
#include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
pollfd结构体定义如下:
struct pollfd {
	int fd;     /* 文件描述符 */
	short events;     /* 等待的事件 */
	short revents;    /* 实际发生了的事件 */
} ; 

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

图片转自:https://blog.csdn.net/lianghe_work/article/details/46534029在这里插入图片描述

返回值和错误代码

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

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

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

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

EINVALnfds参数超出PLIMIT_NOFILE值。

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

代码示例

代码转自:https://blog.csdn.net/gaodes/article/details/82823577
server:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 
#include <sys/select.h>
#include <poll.h>
 
//关于IO复用服务器的poll
 
#define LISTEN_SIZE 1024
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //绑定地址(ip和端口号)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr=INADDR_ANY;
   //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二种写法
   svraddr.sin_port=htons(5555);
   int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("bind error");
   }
   //设置监听参数back login 半连接数最大
   ret=listen(sockfd,1024);
   if(ret<0)
   {
      error_exit("listen error");
   }
   //创建poll结构体
   struct pollfd pollfds[LISTEN_SIZE]={0};
   int i=0;
   for(;i<LISTEN_SIZE;i++)
   {
       pollfds[i].fd=-1;
   }
   pollfds[0].fd=sockfd;
   pollfds[0].events=POLLIN;
   struct sockaddr_in removeaddr;
   int addr_len=sizeof(removeaddr);
   char *buf[1024]={0};
   while(1)
   {
      int nevent=poll(pollfds,LISTEN_SIZE,-1);
      if(nevent==0)
      {
            printf("timeout\n");
            continue;
      }
      else if(nevent<0)
      {
            error_exit("poll error");
      }
      if(pollfds[0].revents&POLLIN)
      {
         int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len);
         if(fd<0)
         {
             error_exit("accept error");
          }
          for(i=1;i<LISTEN_SIZE;i++)
          {
                if(pollfds[i].fd==-1)
                {
                   pollfds[i].fd=fd;
                   pollfds[i].events=POLLIN;
                   break;
                }
          }
          for(i=1;i<LISTEN_SIZE;i++)
          {
                if(pollfds[i].fd==-1)
                {
                    continue;
                } 
                 //判断是否为可读事件
                if(pollfds[i].revents&POLLIN)
                {
                    int rdsize=read(pollfds[i].fd,buf,1024);
                    if(rdsize<=0)
                    {
                        printf("close %d\n",pollfds[i].fd);
                        close(pollfds[i].fd);
                        pollfds[i].fd=-1;
                    }
                    else
                    {
                        printf("read buf =%s,fd=%d\n",buf,pollfds[i].fd);
                    }
                }
          }
      }
   }
}

client:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 
 
//关于客户端的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   if(argc<3)
   {
        printf("run program+ip+port\n");
        return-1;
   }
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //连接服务器,设置服务器的地址(ip和端口)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr= inet_addr(argv[1]);
   svraddr.sin_port=htons(atoi(argv[2]));
   int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("connect error");
   }
 
   write(sockfd,"hello",strlen("hello"));
   sleep(5); 
   close(sockfd);
   return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值