文章目录
- 前言
- 一、poll
- 二、poll使用步骤
- 总结
前言
上一章我们学习了select,但是select作为早期的多路转接接口,缺点十分明显,于是又出现poll和epoll等接口,今天我们就来学习一下poll的使用
提示:以下是本篇文章正文内容,下面案例可供参考
一、poll
参数struct pollfd *fds,它其实传的是一个struct pollfd数组,其结构体成员介绍如下。
- fd设置为要关心的fd;
- events是一个输入型参数,用来告知poll要关心的事件,比如说POLLIN就是让它关心读事件;
- revents是一个输出型参数,当poll检测到关心的fd有events的资源就绪时,就会返回并将该fd对应的revents设置为就绪events。
之前我们使用select,还需要用到一个辅助数组来保存我们需要关心的fds,因为它的大部分参数都是输入输出型参数。而poll采用了struct pollfd结构体的方式让输入输出型参数分离互不影响,也是弥补了select的这一缺点。
参数nfds_t nfds,它用于告知poll需要关心的fd数量,其实就是fds数组的元素个数。
参数int timeout,功能上与select的timeout一样,不过poll舍弃了传struct timeval结构体的方式,直接传一个int整形就可以了,其单位为ms。
二、poll使用步骤
poll的使用步骤与select类似。
#include "Socket.hpp"
#include <poll.h>
#define MAX_POLLFDS 1024
#define INVALID_FD -1
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;
class PollServer
{
public:
PollServer(uint16_t port = default_port)
: _port(port) {}
inline void InitFds()
{
_pollfds[0].fd = _listensock._sockfd;
_pollfds[0].events = POLLIN;
for (int i = 1; i < MAX_POLLFDS; ++i)
{
_pollfds[i].fd = INVALID_FD;
}
}
void Init()
{
_listensock.Init();
_listensock.Bind(AF_INET, default_ip, _port);
_listensock.Listen();
InitFds();
}
void Print()
{
std::cout << "现有fds: ";
for (int i = 0; i < MAX_POLLFDS; ++i)
{
if (_pollfds[i].fd == INVALID_FD)
{
continue;
}
std::cout << _pollfds[i].fd << " ";
}
std::cout << std::endl;
}
void Accepter()
{
struct sockaddr_in tmp;
socklen_t len = sizeof tmp;
int newfd = accept(_listensock._sockfd, (struct sockaddr *)&tmp, &len);
for (int i = 0; i < MAX_POLLFDS; ++i)
{
if (_pollfds[i].fd == INVALID_FD)
{
_pollfds[i].fd = newfd;
_pollfds[i].events = POLLIN;
_pollfds[i].revents = 0;
lg(Info, "Get A New Sockfd:%d", newfd);
break;
}
if (i == MAX_POLLFDS)
{
lg(Warning, "Fds Is Full, Newfd:%d Closed...", newfd);
close(newfd);
return;
}
}
}
void Handler(int fd, int i)
{
char buffer[1024];
memset(buffer, 0, sizeof buffer);
int n = read(fd, buffer, sizeof buffer - 1);
if (n > 0)
{
buffer[n] = 0;
std::string mes = buffer;
std::cout << mes;
_pollfds[i].revents = 0;
}
else if (n < 0)
{
lg(Warning, "Read Error...");
close(fd);
_pollfds[i].fd = INVALID_FD;
}
else
{
lg(Info, "Foreign Host Closed...");
close(fd);
_pollfds[i].fd = INVALID_FD;
}
}
void Dispatcher()
{
for (int i = 0; i < MAX_POLLFDS; ++i)
{
if (_pollfds[i].fd == INVALID_FD)
{
continue;
}
else if (_pollfds[i].revents & POLLIN)
{
if (_pollfds[i].fd == _listensock._sockfd)
{
// accept
Accepter();
continue;
}
Handler(_pollfds[i].fd, i);
}
}
}
void Start()
{
while (1)
{
Print();
int n = poll(_pollfds, MAX_POLLFDS, 5000);
if (n == 0)
{
lg(Info, "Poll Time Out...");
continue;
}
else if (n < 0)
{
lg(Warning, "Poll Error...");
std::cout << "errno:" << errno << " strerror:" << strerror(errno) << std::endl;
}
else
{
Dispatcher();
}
}
}
~PollServer()
{
_listensock.Close();
}
private:
struct pollfd _pollfds[MAX_POLLFDS];
Socket _listensock;
uint16_t _port;
};
总结
poll相比较于select,弥补了两个缺点。
- 不再需要繁琐地更新需要关心的fd和其对应事件。
- 可关心的fd数量不再受其接口内置的数据结构大小限制,可以根据用户需求自由调整。
但是仍然还有缺点,那就是每次进行一次poll都是一次从用户态拷贝数据到内核态的过程。 还有还是需要一些for循环遍历那些已经就绪了的fd。