(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~https://blog.csdn.net/ky233?type=blog
点个关注不迷路⌯'▾'⌯
目录
一、poll函数接口
1.接口
2.poll做了什么工作
3.events和revents的取值
二、代码
三、poll的优缺点
poll将输入参数和输出参数做了分离,所以我们就不用对参数进行重新设定了。并且把上限取消了
一、poll函数接口
1.接口
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
}
参数说明:
- fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返 回的事件集合.
- nfds表示fds数组的长度.
- timeout表示poll函数的超时时间, 单位是毫秒(ms).
2.poll做了什么工作
poll只负责等!
1.用户告诉内核:你帮我关心,哪些fd,哪些事件
2.内核告诉用户:哪些fd,哪些时间已经就绪了
3.events和revents的取值
二、代码
#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 -1using namespace std;
// select 我们只完成读取,写入和异常不做处理 -- epoll(写完整)
class PollServer
{
public:static const int nfds = 100;
public:PollServer(const uint16_t &port = 8080) : _port(port), _nfds(nfds){_listensock = Sock::Socket();Sock::Bind(_listensock, _port);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;}_fds[0].fd = _listensock;_fds[0].events = POLLIN;_timeout = 1000;}void Start(){while (true){int n = poll(_fds, _nfds, _timeout);switch (n){case 0:logMessage(DEBUG, "%s", "time out...");break;case -1:logMessage(WARNING, "select error: %d : %s", errno, strerror(errno));break;default:// 成功的HandlerEvent();break;}}}~PollServer(){if (_listensock >= 0)close(_listensock);if (_fds) delete [] _fds;}
private:void HandlerEvent() // fd_set 是一个集合,里面可能会存在多个sock{for(int i = 0; i < _nfds; i++){// 1. 去掉不合法的fdif(_fds[i].fd == FD_NONE) continue;// 2. 合法的就一定就绪了?不一定if(_fds[i].revents & POLLIN){//指定的fd,读事件就绪// 读事件就绪:连接事件到来,acceptif(_fds[i].fd == _listensock) Accepter();else Recver(i);}}}void Accepter(){string clientip;uint16_t clientport = 0;// listensock上面的读事件就绪了,表示可以读取了// 获取新连接了int sock = Sock::Accept(_listensock, &clientip, &clientport); // 这里在进行accept会不会阻塞?不会!if(sock < 0){logMessage(WARNING, "accept error");return;}logMessage(DEBUG, "get a new line success : [%s:%d] : %d", clientip.c_str(), clientport, sock);int pos = 1;for(; pos < _nfds; pos++){if(_fds[pos].fd == FD_NONE) break;}if(pos == _nfds){// 对struct pollfd进行自动扩容logMessage(WARNING, "%s:%d", "poll server already full,close: %d", sock);close(sock);}else{_fds[pos].fd = sock;_fds[pos].events = POLLIN;}}void Recver(int pos){// 读事件就绪:INPUT事件到来、recv,readlogMessage(DEBUG, "message in, get IO event: %d", _fds[pos]);// 暂时先不做封装, 此时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. 我们也要关闭不需要的fdclose(_fds[pos].fd);// 2. 不要让select帮我关心当前的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. 我们也要关闭不需要的fdclose(_fds[pos].fd);// 2. 不要让select帮我关心当前的fd了_fds[pos].fd = FD_NONE;_fds[pos].events = 0;}}void DebugPrint(){cout << "_fd_array[]: ";for(int i = 0; i < _nfds; i++){if(_fds[i].fd == FD_NONE) continue;cout << _fds[i].fd << " ";}cout << endl;}
private:uint16_t _port;int _listensock;struct pollfd *_fds;int _nfds;int _timeout;
};#endif
三、poll的优缺点
优点:
- 效率高,和select一样
- 节省资源,有大量的连接,只有少量是活跃的
- 输入和输出参数分离,不需要大量的重置
- 参数级别没有fd的上限
缺点:
- poll还是需要遍历,而且不少,在用户层检测时间就绪,与在内核层检测fd就绪,都是一样的
- poll需要用户和内核进行拷贝只需要进行一次,但内核到用户需要一直拷贝,这个时少不了的
- poll编写也不太容易,但比select容易