IO复用之poll

目录

1.poll接口介绍

2.poll和select的不同点

1.关于描述符上限

2.关于事件类型

3.什么事件可读?什么事件可写?

4.poll实现tcp服务器


1.poll接口介绍

接下来介绍IO复用的另一个方法——poll,poll 系统调用和 select 类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。 poll的原型如下:

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

 poll 系统调用成功返回就绪文件描述符的总数,超时返回 0,失败返回-1

  • 第一个参数,fds 参数是一个结构体指针,其实是一个 struct pollfd 结构类型的数组,它指定所有用户感兴趣的文件描述符上发生的可读、可写和异常等事件。
  • 第二个参数nfds 参数是结构体数组的大小,指定被监听事件集合 fds 的大小。
  • 第三个参数timeout 参数指定 poll 的超时值,单位是亳秒(和select不一样,注意)。timeout 为-1 时, poll 调用将永久阻塞,直到某个事件发生,timeout 为 0 时,poll 调用将立即返回。

poll 和select相比,它的事件类型更多,能够支持的文件描述符的数目也更多;

每一个文件描述符,它需要一个结构体pollfd来表示,

pollfd 结构体定义如下:

struct pollfd
{
    int fd;// 文件描述符
    short events;//注册的关注事件类型
    short revents;//实际发生的事件类型,由内核填充;
};

其中, fd 成员指定文件描述符,events 成员告诉 poll 监听 fd 上的哪些事件类型。它是一系列事件的按位或,revents 成员则有内核修改,通知应用程序 fd 上实际发生了哪些事件。

2.poll和select的不同点

poll和select的不同点主要有两点:

1.关于描述符上限

第一个参数给的是一个数组,数组可以超过1024,可以更大(65535甚至更大都是有可能的);而select是用一个fd_set这样的一个集合来收集描述符,这个集合含有1024个位,每个位表示一个描述符,所以select的上限就是1024;

2.关于事件类型

poll 支持的事件类型更多,如下:

3.什么事件可读?什么事件可写?

读不阻塞,我们就认为读事件是就绪的;写不阻塞,我们就认为写事件是就绪的;

对于sockfd来讲,客户端connect连接服务器,那么我们就认为有读事件就绪;对于链接套接字c来讲,接收缓冲区有数据,或者说客户端关闭了套接字(读返回为0,读不阻塞),我们就认为有读事件就绪;

4.poll实现tcp服务器

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <poll.h>
  
#define MAXFD 100

int create_socket()
 {
     int sockfd=socket(AF_INET,SOCK_STREAM,0);
     if(sockfd==-1)
     {
         return -1;
     }
 
     struct sockaddr_in saddr;
     memset(&saddr,0,sizeof(saddr));
     saddr.sin_family=AF_INET;
     saddr.sin_port=htons(6000);
     saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
 
     int res=bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
     if(res==-1)
     {
         return -1;
     }
 
     res=listen(sockfd,5);
     if(res==-1)
     {
         return -1;
     }
 
     return sockfd;
 }

void fds_init(struct pollfd fds[])
{
	for(int i=0;i<MAXFD;i++)
	{
		fds[i].fd=-1;
		fds[i].events=0;
		fds[i].revents=0;
	}
}

void fds_add(struct pollfd fds[],int fd)
{
	for(int i=0;i<MAXFD;i++)
	{
		if(fds[i].fd==-1)
		{
			fds[i].fd=fd;
			fds[i].events=POLLIN;
			fds[i].revents=0;
			break;
		}
	}
}

void fds_del(struct pollfd fds[],int fd)
{
	for(int i=0;i<MAXFD;i++)
	{
		if(fds[i].fd==fd)
		{
			fds[i].fd=-1;
			fds[i].events=0;
			fds[i].revents=0;
		}
	}
}

 int main()
 {
	 int sockfd=create_socket();
	 assert(sockfd!=-1);

	 struct pollfd fds[MAXFD];
	 fds_init(fds);

	 fds_add(fds,sockfd);
	 while(1)
	 {
		 int n=poll(fds,MAXFD,5000);
		 if(n==-1)
		 {
			 continue;
		 }
		 else if(n==0)
		 {
			 printf("time out!\n");
			 continue;
		 }
		 else
		 {
			for(int i=0;i<MAXFD;i++)
			{
				if(fds[i].fd==-1)
				{
					continue;
				}
				if(fds[i].revents & POLLIN)
				{
					if(fds[i].fd==sockfd)
					{
						//accept
						struct sockaddr_in caddr;
						int len=sizeof(caddr);
						int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
						if(c<0)
						{
							continue;
						}
						printf("accept c=%d\n",c);

						fds_add(fds,c);
					}
					else
					{
						//recv
						char buff[128]={0};
						int res=recv(fds[i].fd,buff,127,0);
						if(res<=0)
						{
							close(fds[i].fd);
							fds_del(fds,fds[i].fd);
							printf("one clinet over!\n");
							continue;
						}
						else
						{
							printf("buff(c=%d)=%s\n",fds[i].fd,buff);
							send(fds[i].fd,"ok",2,0);
						}
					}
				}
			}
		 }
	 }
	 exit(0);
 }

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux IO复用是指在处理多个I/O事件时,通过一种机制使得一个进程可以同时监听多个I/O操作,从而提高程序的效率和性能。 在Linux系统中,常用的IO复用机制有三种:select、poll和epoll。 1. select:select函数是最早引入的IO复用机制之一,它通过传入一组文件描述符集合,来监听这些文件描述符上是否有事件发生。当其中任意一个文件描述符上有事件发生时,select函数就会返回,然后程序可以通过遍历文件描述符集合来判断哪些文件描述符上有事件发生。 2. pollpoll函数是对select的改进,其使用方式和select类似。不同的是,poll函数使用一个pollfd结构数组来存储待监听的文件描述符及其对应的感兴趣事件,通过调用poll函数时传入这个数组来实现IO复用。相对于select,poll没有最大文件描述符数量的限制,并且效率更高。 3. epoll:epoll是Linux下最新的IO复用机制,它提供了更加高效的IO事件通知机制。epoll使用一个文件描述符来管理被监听的其他文件描述符,通过调用epoll_ctl函数向这个文件描述符中注册或者删除需要监听的文件描述符。当某个文件描述符上有事件发生时,epoll_wait函数会返回该文件描述符的相关信息给程序处理。相对于select和poll,epoll在处理大量连接时具有更好的性能。 总结来说,Linux IO复用机制可以让一个进程同时监听多个I/O事件,避免了使用阻塞IO时的等待时间,提高了程序的效率和性能。而select、poll和epoll是常用的IO复用机制。其中,epoll是效率最高的一种机制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值