您的位置:首页 > 其它

I/O多路转接   ----   poll

2016-06-03 20:57 357 查看
一、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: 已经发生的事件, 设置标志 来反映相关条件的存在 常量 说明 POLLIN 普通或优先级带数据可读 POLLRDNORM 普通数据可读 POLLRDBAND 优先级带数据可读 POLLPRI 高优先级数据可读 POLLOUT 普通数据可写 POLLWRNORM 普通数据可写 POLLWRBAND 优先级带数据可写
// 只能作为描述字的返回结果存储在revents中 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件
struct pollfd
{    int   fd;         /* 文件描述符 */
short events;     /* 请求的事件 */
short revents;    /* 返回的事件 */
};
//nfds: 要监视的描述符的数目//timeout:指定poll在返回前没有接收事件时应该等待的时间【单位:ms】。 INFTIM: 永不超时 0 :立即返回
>0: 等待指定的时间
2、特点: pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。 select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。三、poll 服务器
#include <stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/select.h>
#include<sys/poll.h>

#define _BACKLOG_ 5
#define _MAX_NUM_ 64
void usage(const char *proc)
{
printf("Usage: %s [ip][port]\n",proc);
}
static int startup(const char *ip,const int port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(1);
}
int opt=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip);

if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
perror("bind");
exit(2);
}
if(listen(sock,_BACKLOG_) < 0)
{
perror("listen");
exit(3);
}
return sock;
}
static int poll_server(int listen_sock)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);

struct pollfd fds[_MAX_NUM_];
fds[0].fd=listen_sock;
fds[0].events=POLLIN;
fds[0].revents=0;

size_t i=0;
for(;i<_MAX_NUM_;++i)
{
fds[i].fd=-1;
fds[i].events=0;
fds[i].revents=0;
}
int max_fd=1;
int timeout=5000;
while(1)
{
switch(poll(fds,max_fd,timeout))
{
case -1://error
perror("poll");
break;
case 0://timeout
printf("poll timeout");
break;
default://normal
{
for(i=0;i<_MAX_NUM_;++i)
{
//is listen events ready?
if(fds[i].fd == listen_sock && fds[i].revents == POLLIN)
{
int accept_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
if(accept_sock < 0)
{
perror("accept");
continue;
}
printf("get a client...[ip: %s][port: %d]\n",\
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
for(i=0;i<_MAX_NUM_;++i)
{
if(fds[i].fd == -1)
{
fds[i].fd=accept_sock;
fds[i].events=POLLIN;
max_fd++;
break;
}
}
if(i == _MAX_NUM_)
{
close(accept_sock);
}
}
else if(fds[i].fd > 0 && fds[i].revents == POLLIN)
{
char buf[1024];
ssize_t _size=read(fds[i].fd,buf,sizeof(buf)-1);
if(_size > 0)//read success
{
buf[_size]='\0';
printf("Client # %s",buf);
}
else if(_size == 0)//client close
{
printf("clint close...\n");
struct pollfd tmp=fds[i];
fds[i]=fds[max_fd-1];
close(fds[max_fd-1].fd);
fds[max_fd-1].fd=-1;
fds[max_fd-1].events=0;
fds[max_fd-1].revents=0;
--max_fd;
}

}
else
{}
}
}
break;
}
}
return 0;
}
int main(int argc,char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
char *ip=argv[1];
int port=atoi(argv[2]);
int listen_sock=startup(ip,port);
poll_server(listen_sock);
close(listen_sock);
return 0;
}
总结:
poll在返回后,需要通过遍历文件描述符来获取已经就绪的socket从而进行下一步操作;而且使用完监听套接字后,都需要进行关闭。

本文出自 “花开彼岸” 博客,请务必保留此出处http://zxtong.blog.51cto.com/10697148/1785978
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: