多路转接模型之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;
}



posted @ 2017-05-12 13:40  mfmdaoyou  阅读(194)  评论(0)    收藏  举报