多路转接模型之poll

poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll和select效率差不多,只是其使用接口相对简单些,poll不在局限于1024个文件描述符,poll监听事件和触发事件分开,event表示监听事件,revents表示触发的事件。相比select不用每一次都需要重新设置监听事件。

 #include <poll.h>

 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//第一个参数是struct pollfd数组
struct pollfd
{
     int   fd;   /* file descriptor */你要监控文件描述符
     short events;   /* requested events */ 监听文件描述符上的事件 传入参数由用户设置
     short revents;    /* returned events */监控文件描述符事件返回值 传出参数由内核设置
};

POLLIN普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
POLLRDNORM-数据可读
POLLRDBAND-优先级带数据可读
POLLPRI 高优先级可读数据
POLLOUT普通或带外数据可写
POLLWRNORM-数据可写
POLLWRBAND-优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

第二个参数,指结构体数组长度。
timeout 毫秒级等待
-1:阻塞等,#define INFTIM -1 Linux中没有定义此宏
0:立即返回,不阻塞进程
>0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值

poll server端实例:

#include<stdio.h>
#include<string.h>
#include<poll.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<errno.h>
#define OPEN_MAX 1024

int create_listen(int port)
{
	int listen_st,on;
	struct sockaddr_in s_addr;
	listen_st =socket(AF_INET,SOCK_STREAM,0);
	if(listen_st==-1)

	{
		perror("socket error ");
		return -1;
	}
	if(setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1)
	{
		perror("setsockopt error");
		return -1;
	}
	s_addr.sin_port=htons(port);
	s_addr.sin_family=AF_INET;
	s_addr.sin_addr.s_addr=htonl(INADDR_ANY);

	if(bind(listen_st,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1)
	{
		perror("bind error");
		return -1;
	}
	if (listen(listen_st, 5) == -1) // 设置文件描述符具有监听的功能
    {
        perror("listen error");
        return -1;
    }
    return listen_st;
}

int run_server(int port)
{
	int i,maxi,listen_st,conn_st,sockaddr_len;
	int nready;
	struct pollfd client[OPEN_MAX];
	char buf[1024];
	struct sockaddr_in c_addr;
	listen_st=create_listen(port);
	if(listen_st==-1)
	{
		return -1;
	}
	for(i=1;i<OPEN_MAX;i++)
	{
		client[i].fd=-1;
	}
	client[0].fd=listen_st;
	client[0].events=POLLIN;
	maxi=0;
	while(1)
	{
		nready = poll(client,maxi+1,-1);//poll 阻塞
		if(nready<0)
		{
			perror("poll error");
			break;
		}
		if((client[0].revents&POLLIN))//检测listen_st
		{
			sockaddr_len=sizeof(c_addr);
			conn_st=accept(listen_st,(struct sockaddr *)&c_addr,&sockaddr_len);
			printf("received form %s at port:%d \n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
			for(i=0;i<OPEN_MAX;i++)
			{
				if(client[i].fd<0)
				{
					client[i].fd=conn_st;
					client[i].events=POLLIN;
					break;
				}
			}
			if(i==OPEN_MAX)
			{
				printf("too many client \n");
				close(conn_st);
			}else
			{
				if(i>maxi) //记录最大下标
				{
					maxi=i;
				}
			}
			if(--nready==0) continue;
		}

		for(i=1;i<=maxi;i++)
		{
			if((conn_st=client[i].fd)<0)
			{
				continue;
			}

			if(client[i].revents&POLLIN)
			{
				memset(buf,0,sizeof(buf));
				int rv=read(conn_st,buf,sizeof(buf));
				if(rv<0)
				{
					if(errno==ECONNRESET)/* 当收到RST标志时*/
					//这种错误是由于客户端发过FIN ACk掉线了客服端进程已经结束了 服务端再发FIN 客户端会发送RST
					{
						printf("client aborted connection \n");
						close(conn_st);
						client[i].fd=-1;
					}
				}
				else if(rv==0)
				{
					printf("close client \n");
					close(conn_st);
					client[i].fd=-1;
				}
				else
				{
					printf("recv from client:%s \n",buf);
					write(conn_st,buf,strlen(buf));
				}
				if (--nready == 0) break;  //就绪个数减一
			}

		}
	}
	close(listen_st);
	return 0;
}

int main(int argc,char *argv[])
{
	if(argc<2)
	{
		printf("usage:%s port \n",argv[0]);
		return 0;
	}
	int port=atoi(argv[1]);
	if(port==0)
	{
		printf("port error \n");
		return 0;
	}
	printf("start server \n");
	run_server(port);
	return 0;
}

版权声明:欢迎转载,如有不足之处,恳请斧正。

时间: 2024-07-29 15:24:23

多路转接模型之poll的相关文章

高性能服务器——I/O多路转接的三种模式(select &poll& epoll)

一.简单的服务器I/O模型 最简单的的TCP服务器,有三种模式: 1.单执行流,一个server端连接一个client端 2.多进程,一个server端通过多进程的方式,每个进程连接一个client端 3.多线程,一个server端通过多进程的方式,每个线程连接一个client端 (http://zhweizhi.blog.51cto.com/10800691/1830267) 这里实现过 要提升服务器性能,其实就是想要让一个server端能在负载允许的情况下,连接尽可能多的client端. 因

【Nginx】I/O多路转接之select、poll、epoll

从socket中读取数据可以使用如下的代码: while( (n = read(socketfd, buf, BUFSIZE) ) >0) if( write(STDOUT_FILENO, buf, n) = n) { printf(“write error”); exit(1); } 当代码中的socketfd描述符所对应的文件表项是处于阻塞时,它会一直阻塞,直到有数据从网络的另一端发送过来.如果它是一个服务器程序,它要读写大量的socket,那么在某一个socket上的阻塞很明显会影响与其它

I/O多路转接select/poll/epoll

I/O多路转接(多路复用)又被称为“事件驱动”,是操作系统提供的一个功能,当你关心的文件(如socket)可读.可写时(称为事件就绪)采用某种方式通知你,只有收到通知时你才去执行read/write操作,这样在每次读或写时就不会阻塞,即I/O操作中等的部分交给操作系统内核去完成,而read/write之类的操作只需要在事件就绪时完成数据拷贝.等的过程由select/poll/epoll等系统调用触发,这些函数可同时监视多个描述符上的事件是否就绪,因此可以在一个线程内不发生阻塞的交替完成多个文件的

I/O多路转接-epoll

By francis_hao    Aug 5,2017 APUE讲多路转接的章节介绍了select.pselect和poll函数.而epoll是linux内核在2.5.44引入的.在glibc 2.3.2添加了支持. epoll_create – 打开一个epoll文件描述符 epoll_ctl – 控制epoll文件描述符接口 epoll_wait – 在epoll文件描述符上等待一个I/O事件 概述 #include <sys/epoll.h>int epoll_create(int s

IO多路转接

IO多路转接的技术可以避免阻塞IO的弊端,因为我们有时候需要在多个描述符上读read.写write,如果使用阻塞IO,就有可能长时间阻塞在某个描述符上而影响其它描述符的使用. 关于阻塞IO的处理办法,可以考虑一下几个方案: 1.多进程.弊端是多个进程终止时的通信,增加了程序的复杂度. 2.多线程.弊端是多个线程之间的同步,同样也增加了程序的复杂度. 3.轮询polling.使用非阻塞IO读取数据,弊端是浪费CPU时间,在多任务系统中应当避免使用这种方法. 4.异步IO.用到了信号机制,如系统V的

I/O多路转接

对于多个非阻塞I/O,怎么知道I/O何时已经处于可读或可写状态? 如果采用循环一直调用write/read,直到返回成功,这样的方式成为轮询(polling).大多数时间I/O没有处于就绪状态,因此这样的轮询十分浪费CPU. 一种比较好的技术是使用I/O多路转接,也叫做I/O多路复用.其基本思想为:先构造一个有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已经准备好进行I/O时,该函数才返回.在返回时,它告诉进程哪些描述符已经准备好可以进行I/O了. 有3个函数poll.pselect

select函数与I/O多路转接

相作大家都写过读写IO操作的代码,例如从socket中读取数据可以使用如下的代码: while( (n = read(socketfd, buf, BUFSIZE) ) >0) if( write(STDOUT_FILENO, buf, n) = n) { printf("write error"); exit(1); } 当代码中的socketfd描述符所对应的文件表项是处于阻塞时,它会一直阻塞,直到有数据从网络的另一端发送过来.如果它是一个服务器程序,它要读写大量的socke

I/O多路转接 &#160; ---- &#160; poll

一.poll poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多. 二.poll相关函数 #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); //fds: pollfd结构体 events: 要监视的事件 revents: 已经发生的事件,  设置标志 来反映相关条件的存在 常量            

I/O多路转接之poll

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现. pollfd结构包含了要监视的event和发生的event,不再使用select"参数-值"传递的方式.同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降). 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符. 从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket.事 实上,同时连接的大量客户端在一