poll f服务器
来源:互联网 发布:算法工程师笔试考什么 编辑:程序博客网 时间:2024/06/05 18:23
1.poll监听的文件描述符没有最大数量的限制
2.poll对于select来说包含了一个pollfd结构,pollfd结构包含了要监视的event和发生的revent,而不像select那样使用输入输出的传递方式。所以不需要每次监听都初始化
poll缺点:
1.数量过大以后其效率也会线性下降。
2.poll和select一样也是返回就绪事件的个数,需要遍历文件描述符来判断是那个事件已经就绪,当数量很大时,开销也就很大。
3.select和poll都只能工作在低效的LT(水平触发)模式
4.每次调用poll,都需要把pollfd数组从用户态拷贝到内核态,这个开销在fd很多时会很大
5.内核采用轮询(遍历pollfd数组)的方式来检测就绪事件,这个开销在fd很多时也很大
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
1
1
fds是一个pollfd的结构体数组。
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
1
2
3
4
5
6
1
2
3
4
5
6
这就是这个结构体数组每个元素。fd用来记录对应的文件描述符,events用来表示poll所监听的事件,这个由用户来设置。revents用来表示返回的事件。revents是通过内核来进行操作修改。
这里提供了一些合法事件。
事件说明
POLLIN普通或优先级带数据可读
POLLRDNORM普通数据可读
POLLRDBAND优先级带数据可读
POLLPRI高优先级数据可读
POLLOUT普通数据可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带数据可写
POLLERR发生错误
POLLHUP发生挂起
POLLNVAL描述字不是一个打开的文件
后面的三个参数在events无意义,只能作为返回结果存储在revents。
另外,这里需要说的,这些参数如何设置给events,这些宏相当于每一个占用一个比特位,我们可以去想一下位图,所以,如果我们要进行设置两个事件,就使用|操作,另外,当我们去查看事件是否发生的时候,这个时候我们可以使用revents&事件,如果事件发生了,那么结果大于1。这就是一个简单的位运算的,相信你仔细想想就能够理解。
第二个参数nfds,用来监视的文件描述符的数目。
第三个参数是timeout,用来设置超时时间。
参数说明
-1poll将永远阻塞,等待知道某个时间发生
0立即返回
大于0的值设置正常时间值
返回值
poll返回revents不为0的文件描述符的个数。
失败返回-1
总结
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
poll使用了events和revents分流的特点,这样可以使得对关心事件只进行注册一次。
poll基于链表进行存储,没有最大连接数的限制,只取决于内存大小。
poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。
poll的缺点
1、大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样是不是有意义。
2、poll依然需要进行轮询,所消耗的时间太多。
3、水平触发,效率低
代码:
[cpp] view plain copy
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<poll.h>
#include<string.h>
static void usage(const char *str)
{
printf("Usage:%s [serv_ip] [serv_port]\n",str);
}
static int startup(const char *ip,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 serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
int ret = bind(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(ret < 0)
{
perror("bind");
exit(2);
}
ret = listen(sock,128);
if(ret < 0)
{
perror("listen");
exit(3);
}
return sock;
}
// poll函数第一个参数类型。
//struct pollfd {
// int fd; /* file descriptor */
// short events; /* requested events */
// short revents; /* returned events */
//};
//
int main(int argc ,char *argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = startup(argv[1],atoi(argv[2]) );
struct pollfd peerfd[1024]; //当有就绪事件发生的话会写到这个数组里面。
peerfd[0].fd = sock; //首先监听连接套接字。监听它的读事件。
peerfd[0].events = POLLIN;
int timeout = -1; //表示阻塞式等待。
int i = 1;
for(; i < 1024; ++i)
{
peerfd[i].fd = -1; //表示这个位置么有被占用。
}
while(1)
{ //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int ret = 0;
switch(ret = poll(peerfd,1024,timeout))
{
case 0:
printf("timeout...\n");
break;
case -1:
perror("poll");
break;
default:
{
int i = 0;
for(i = 0; i < 1024; ++i)//遍历数组查看有哪些就绪事件发生了。
{
if(i == 0 && (peerfd[i].revents & POLLIN) )//有新的连接请求。
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(sock,(struct sockaddr*)&client,&len);
if(new_sock < 0)
{
perror("accept");
continue;
}
printf("get a new client\n");
int j = 1;
for(; j < 1024; ++j)
{
if(peerfd[j].fd < 0) //找最小的未被使用的位置。
{
peerfd[j].fd = new_sock;
peerfd[j].events = POLLIN;
break;
}
}
if(j == 1024 )
{
printf("too many client...\n");
close(new_sock);
}
} //if
else if(i != 0)
{
if(peerfd[i].revents & POLLIN) //客户端有读事件发生。
{
char buf[1024];
ssize_t s = read(peerfd[i].fd,buf,sizeof(buf) - 1);
if(s > 0)
{
buf[s] = 0;
printf("clinet say:%s\n",buf);
peerfd[i].events = POLLOUT; //读完后监听写事件。
}
else if(s <= 0)
{
close(peerfd[i].fd);
peerfd[i].fd = -1;
}
}
else if(peerfd[i].revents & POLLOUT)//写事件就绪。
{
/* 客户端写事件发生 */
/* 使用浏览器测试,写回到客户端,浏览器会解析字符串,显示 Hellp Epoll! */
const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><br/><h1>Hello poll!</h1></html>";
write(peerfd[i].fd, msg, strlen(msg));
//写完后关闭套接字。
close(peerfd[i].fd);
peerfd[i].fd = -1;
}
} //else if
} //for
} //default
} //switch()
} //while(1)
return 0;
}
- poll f服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器
- poll服务器与客户端
- Linux poll服务器
- poll服务器编程综合
- poll->epoll服务器
- 并发服务器poll
- poll服务器---多路连接之 poll
- 用poll 实现并发服务器
- 49-使用 poll 改写服务器
- grpc应用于微服务的分析,基于python
- JAVA 线程池
- S2分班考试笔试题总结
- MySQL创建高性能的索引
- boost log库学习使用三(输出日志到文件)
- poll f服务器
- Linux下网络性能评估
- eclipse中工程树形界面后方显示svn提交人(转)
- C++中的类和对象
- 【量化投资】基金择时策略浅析(2)-有择时能力的基金
- Django中Request和Response系列之十
- Phantomjs模拟滑动验证码
- DOS命令大全
- 分班考试笔试题解析(2017/6/28)