poll接口介绍
参数
fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.
nfds表示fds数组的长度.
timeout表示poll函数的超时时间, 单位是毫秒(ms).返回值
返回值小于0, 表示出错;
返回值等于0, 表示poll函数等待超时;
返回值大于0, 表示poll由于监听的文件描述符就绪而返回.
events和revents的取值:
poll和select的区别
-
文件描述符集合的表示方式
- select使用位图来表示文件描述符集合,这导致了它有一个固定的上限(通常由sizeof(fd_set)决定)。
- poll使用结构体数组来表示文件描述符和事件类型,因此没有固定的上限,只受限于系统资源(如内存)。
-
重新设置文件描述符集合
- 每次调用select之前,都需要重新设置文件描述符集合,因为select在返回时会修改这个集合(将未就绪的文件描述符清零)。
- poll则不需要重新设置文件描述符集合,因为它使用的是结构体数组,每次调用poll时都会传入完整的数组信息。
-
监视的事件类型
- select可以监视读、写、异常等事件类型,但每次调用时都需要分别设置对应的文件描述符集合。
- poll也可以监视多种事件类型,并且可以在一个结构体元素中同时设置多个事件类型(通过位或运算)。
-
性能
- 在文件描述符数量较少的情况下,select和poll的性能相差不大。
- 但在文件描述符数量较多的情况下,poll可能会比select更高效,因为它避免了每次调用时都需要重新设置文件描述符集合的开销。
poll样例代码
其他代码文件在这 Poll代码
PollServer.hpp
#pragma once
#include <iostream>
#include <poll.h>
#include "Socket.hpp"
#include "log.hpp"
using namespace std;
static const uint16_t defaultport = 8080;
static const int fd_num_max = 64;//这个是可以修改的,不像select是固定的
int defaultfd = -1;
int non_event = 0;
class PollServer
{
public:
PollServer(const uint16_t port = defaultport) : _port(port)
{
for (int i = 0; i < fd_num_max; i++)
{
_event_fds[i].fd = -1;
_event_fds[i].events = non_event;
_event_fds[i].revents = non_event;
}
}
void Init()
{
_listensock.Socket();
_listensock.Bind(_port);
_listensock.Listen();
}
void Accepter()
{
uint16_t clientport;
string clientip;
int fd = _listensock.Accept(&clientport, &clientip);
if (fd < 0)
return;
int i = 0;
for (; i < fd_num_max; i++)
{
if (_event_fds[i].fd!= defaultfd)
continue;
else
break;
}
if (i == fd_num_max) // 数组满了,poll fd满了
{
log(Warning, "server is full, close %d now!", fd);
close(fd);
//可以进行扩容
}
else
{
_event_fds[i].fd = fd;
_event_fds[i].events=POLLIN;
_event_fds[i].revents = non_event;
PrintFd();
}
}
void Recver(int fd, int pos)
{
char buffer[1024];
ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // 有bug,因为应用层没有定制协议,不一定能读到完整的报文,我们这里不定制先
if (n > 0)
{
buffer[n] = 0;
cout << "get a messge: " << buffer << endl;
}
else if (n == 0)
{
log(Info, "client quit, me too, close fd is : %d", fd);
close(fd);
_event_fds[pos].fd= defaultfd;
}
else
{
log(Warning, "recv error: fd is : %d", fd);
close(fd);
_event_fds[pos].fd = defaultfd;
}
}
void Dispatcher()
{
for (int i = 0; i < fd_num_max; i++)
{
int fd = _event_fds[i].fd;
if (fd == defaultfd)
continue;
if (_event_fds[i].revents&POLLIN)
{
// 就绪的文件描述符
if (fd == _listensock.GetFd()) // 如果是listen的fd,就接收连接,添加到数组中
{
Accepter();
}
else // 进行网络通信的fd了
{
Recver(fd, i);
}
}
}
}
void Start()
{
//先把listensock添加到数组中
_event_fds[0].fd = _listensock.GetFd();
_event_fds[0].events = POLLIN;
int timeout=2000;
while (1)
{
// accept?不能直接accept!检测并获取listensock上面的事件,新连接到来,等价于读事件就绪
// 这里不能直接accept,因为可能还没有新的连接
int n = poll(_event_fds, fd_num_max, timeout);
if (n < 0)
{
cerr << "poll error" << endl;
}
else if (n == 0)
{
cout << "time out, timeout: " <<timeout << endl;
}
else
{
// 有事件就绪了
cout << "get a new link!!!!!" << endl;
Dispatcher(); // 给就绪的fd分配任务
}
}
}
void PrintFd()
{
cout << "online fd list: ";
for (int i = 0; i < fd_num_max; i++)
{
if (_event_fds[i].fd== defaultfd)
continue;
cout << _event_fds[i].fd << " ";
}
cout << endl;
}
~PollServer()
{
}
private:
Sock _listensock;
uint16_t _port;
struct pollfd _event_fds[fd_num_max]; // 数组, 用户维护的!
};