I/O复用--》poll

本文深入探讨了poll系统调用的原理与应用,对比select,poll提供了更丰富的事件类型,分离用户关注事件与内核就绪事件,扩大了文件描述符的范围。通过示例代码展示了如何使用poll进行网络编程,实现多客户端连接处理。

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

poll

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

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

     fds:是一个 pollfd 结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。传递的是数组首地址,数组由用户定义。
     nfds:数组的长度,元素的个数,用户关注的文件描述符的个数
     timeout:超时时间 -1 代表永久阻塞直到有就绪事件发生
返回值
    -1 出错
    =0 超时
    >0 就绪文件描述符的个数

pollfd 结构体的定义如下:

	struct pollfd
	{
		int fd;   用户关注的文件描述符
		short events;  注册的文件描述符上的那种事件
		short revents;  由内核修改,返回此文件描述符上发生的事件类型(必须是events指定的关注的事件)
	};

在这里插入图片描述
poll 和 select 的不同点
1、用户关注的事件类型更多
2、将用户关注的事件与内核修改的就绪事件分隔开,每次调用 poll 不需要重新设置
3、文件描述符不再是按位表示,直接用 int 类型,文件描述符的取值范围扩大到系统最大限制 655353。
     3.1 用户关注的文件描述符可以更大
     3.2 用户关注的文件描述符个数由用户自己制定,能扩大到系统最大限制

1、poll 返回时,也是将用户关注的所有文件描述符返回。poll 探测就绪文件描述符的时间复杂度是 O(n),poll 返回后依旧需要循环检测哪些文件描述符
2、内核依旧是采用轮询方式处理

Sever

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

#define FDMAXNUM 100

void InitFds(struct pollfd *fds)
{
	int i=0;
	for(;i<FDMAXNUM;i++)
	{
		fds[i].fd=-1;
	}
}

void DeleteFd(int fd, struct pollfd *fds)
{
	int i=0;
	for(;i<FDMAXNUM;i++)
	{
		if(fds[i].fd==fd)
		{
			fds[i].fd=-1;
			break;
		}
	}
}

void AddFd(int fd, struct pollfd *fds)
{
	int i=0;
	for(;i<FDMAXNUM;i++)
	{
		if(fds[i].fd==-1)
		{
			fds[i].fd=fd;
			fds[i].events=POLLIN|POLLRDHUP;
			break;
		}
	}
}

void GetClientLink(int fd, struct pollfd *fds)
{	
	struct sockaddr_in cli;
	int len=sizeof(cli);

	int c=accept(fd,(struct sockaddr*)&cli,&len);
	if(c==-1)
	{
		return;
	}
	printf("accept(%d)\n",c);
	AddFd(c,fds);
}

void DealClientData(int fd,  struct pollfd *fds, int rdhup)	
{
	if(rdhup)
	{
		close(fd);
		printf("one client over\n");
		DeleteFd(fd,fds);
		return;
	}

	char buff[128]={0};
	int n=recv(fd,buff,127,0);
	if(n<=0)
	{
		DeleteFd(fd,fds);
		return ;
	}

	printf("buff=%s",buff);
	send(fd,"OK",2,0);
}

int main()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(listenfd != -1);

	struct sockaddr_in ser;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	listen(listenfd, 5);

	struct pollfd  fds[FDMAXNUM];
	InitFds(fds);
	fds[0].fd=listenfd;
	fds[0].events=POLLIN;

	while(1)
	{
		int a=poll(fds,FDMAXNUM,-1);
		if(a<=0)
		{
			perror("poll error\n");
			exit(0);
		}
		if(a>0)
		{
			int i=0;
			for(;i<FDMAXNUM;i++)
			{
				if(fds[i].fd==-1)
				{
					continue;
				}
				if(fds[i].fd==listenfd && fds[i].revents & POLLIN)
				{
					 GetClientLink(fds[i].fd, fds);
				}
				else if(fds[i].revents & POLLIN)
				{
					 if(fds[i].revents & POLLRDHUP)
						 DealClientData(fds[i].fd,fds,1);
					 else
						 DealClientData(fds[i].fd,fds,0);
				}
			}
		}
	}
}

Cli

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>//字节序列转换函数所用
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//地址转换函数所用

int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(-1!=sockfd);

	struct sockaddr_in ser;
	memset(&ser,0,sizeof(ser));
	ser.sin_family=AF_INET;
	ser.sin_port=htons(6000);
	inet_aton("127.0.0.1",(struct in_addr*)&ser.sin_addr);

	int res=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	assert(-1!=res);
	
	while(1)
	{
		printf("please input: ");
		char data[128]={0};
		fgets(data,128,stdin);
		if(strncmp(data,"bye",3)==0)
		{
			close(sockfd);
			break;
		}

		send(sockfd,data,strlen(data)+1,0);
		char buff[128]={0};
		int n=recv(sockfd,buff,127,0);
		if(n<=0)
		{
			printf("error\n");
			close(sockfd);
			break;
		}
		printf("n==%d: %s\n",n,buff);
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值