文章目录
- 前言
- 一、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 -1const 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){// acceptAccepter();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。