1. poll
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
poll只负责等。
参数介绍
fds
是一个结构体类型的地址,相比于select中的fd_set类型,pollfd结构体可以内部封装一些遍历,解决需要关系那些文件描述符,以及哪些文件描述符就绪了。从而解决在select中一直需要重新设定的问题。
nfds
表示fds数组的长度
timeout
单位是毫秒,不再是一个timeval类型的地址,变为了拷贝。所以也就不需要再对timeout重新设定,单位是毫秒。
返回值
返回就绪的文件描述符个数。
1.2 struct pollfd 结构体
struct pollfd
{
int fd;//文教描述符
short events;//告诉内核我要你关心哪些事件
short revents;//告诉用户,内核中这些事件就绪了
};
events和revents的取值
其中常用的 POLLIN POLLOUT POLLERR就对应着select中的那三个参数
1.3.demo
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
struct pollfd poll_fd;
//让poll帮我关心 0号文件描述符 关心事件为输入事件
poll_fd.fd=0;
poll_fd.events=POLLIN;
for(;;)
{
int ret=poll(&poll_fd,1,1000);
if(ret<0)
{
perror("poll");
continue;
}
else if(ret==0)
{
printf("poll timeout\n");
continue;
}
if(poll_fd.revents==POLLIN)
{
printf("poll event already\n");
char buffer[1024];
read(0,buffer,sizeof (buffer) -1);
printf("%s",buffer);
}
}
return 0;
}
2.基于select测试改写的poll代码
select版本
Linux--高级IO--select--0326_Gosolo!的博客-CSDN博客
#pragma once
#ifndef __POLL_SVR_H__
#define __POLL_SVR_H__
#include <iostream>
#include <string>
#include <vector>
#include <poll.h>
#include <sys/time.h>
#include "log.hpp"
#include "Sock.hpp"
#define FD_NONE -1
using namespace std;
class PollServer
{
public:
static const int nfds=100;
PollServer(const uint16_t &port=8080)
:_port(port)
,_nfds(nfds)
,_timeout(1000)
{
_listensock=Sock::Socket();
Sock::Bind(_listensock,_port);//ip缺省值为 0.0.0.0
Sock::Listen(_listensock);
logMessage(DEBUG,"%s","create base socket success");
_fds=new struct pollfd[_nfds];
for(int i=0;i<_nfds;i++)
{
_fds[i].fd=FD_NONE;
_fds[i].events=_fds[i].revents=0;
}
//做一个规定 _fd_array[0]=_listensock
_fds[0].fd=_listensock;
_fds[0].events=POLLIN;
}
void Start()
{
while(true)
{
int n=poll(_fds,_nfds,_timeout);
switch(n)
{
case 0:
logMessage(DEBUG,"time out...");
break;
case -1:
logMessage(WARNING,"poll errno: %d : %s",errno,strerror(errno));
break;
default:
//成功
HandlerEvent();
break;
}
}
}
~PollServer()
{
if(_listensock>=0) close(_listensock);
if(_fds) delete[] _fds;
}
private:
void HandlerEvent()
{
for(int i=0;i<_nfds;i++)
{
//没让select关心这个文件
if(_fds[i].fd==FD_NONE) continue;
//让关心了 但是需要知道他是否就绪
if(_fds[i].revents & POLLIN)
{
if(_fds[i].fd==_listensock)
{
Acceptr();
}
else
{
Recver(i);
}
}
}
}
void Acceptr()
{
string clientip;
uint16_t clientport=0;
int sock=Sock::Accept(_listensock,&clientip,&clientport);
if(sock<0)
{
logMessage(WARNING,"accept error");
return;
}
logMessage(DEBUG,"get a new link success :[%s:%d] : %d",clientip.c_str(),clientport,sock);
//找一个位置添加 我刚刚得到的sock套接字 好让select帮我关心
int pos=1;
for(;pos<_nfds;pos++)
{
if(_fds[pos].fd==FD_NONE) break;
}
if(pos==_nfds)
{
//也可以在这里扩容
logMessage(WARNING,"%s:%d","select server already full,close fd: %d",sock);
close(sock);
}
else
{
_fds[pos].fd=sock;
_fds[pos].events=POLLIN;
}
}
void Recver(int pos)
{
// 读事件就绪:INPUT事件到来、recv,read
logMessage(DEBUG, "message in, get IO event: %d", _fds[pos].fd);
// 暂时先不做封装, 此时select已经帮我们进行了事件检测,fd上的数据一定是就绪的,即 本次 不会被阻塞
// 这样读取有bug吗?有的,你怎么保证以读到了一个完整包文呢?
char buffer[1024];
int n = recv(_fds[pos].fd, buffer, sizeof(buffer)-1, 0);
if(n > 0)
{
buffer[n] = 0;
logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
}
else if(n == 0)
{
logMessage(DEBUG, "client[%d] quit, me too...", _fds[pos].fd);
// 1. 我们也要关闭不需要的fd
close(_fds[pos].fd);
// 2. 不要让poll帮我关心当前的fd了
_fds[pos].fd = FD_NONE;
_fds[pos].events=0;
}
else
{
logMessage(WARNING, "%d sock recv error, %d : %s", _fds[pos].fd, errno, strerror(errno));
// 1. 我们也要关闭不需要的fd
close(_fds[pos].fd);
// 2. 不要让select帮我关心当前的fd了
_fds[pos].fd = FD_NONE;
}
}
private:
uint16_t _port;
int _listensock;
struct pollfd* _fds;
int _nfds;//最大数量
int _timeout;
};
#endif
2.2测试及结果
3.poll的优缺点
优点:
效率高。
适用于有大量连接,但只有少量的是活跃的。节省资源。
输入输出参数分离的,不需要进行大量的重置。
poll没有fd管理上限。
缺点:
poll依旧需要不少遍历,在用户层检测就绪,与内核检测fd就绪,都是一样。
poll需要内核到用户的拷贝。
poll代码也比较复杂。但比select容易。