本篇总结的是poll的相关内容,在总结poll的内容前,先回顾一下select的缺点

select的缺点

select的缺点也比较明显

  1. 等待的fd是有上限的,在我们当前这个版本来说,它能等待的最大值是1024,也就是说超过来了这个1024我们的处理方式是直接把链接的这个socket丢弃
  2. 输入输出型参数比较多,数据拷贝的频率比较高
  3. 输入输出型参数比较多,每次都要对关心的fd进行事件重置
  4. 在用户层来说,在使用第三方数组进行管理fd的时候,要进行很多次的遍历,在内核中检测fd的事件就绪的时候,也要进行遍历

那么为了解决上述的这些问题,我们引入了poll的概念:

poll

先看一下poll的相关接口

在这里插入图片描述
poll的接口和select的接口几乎一样,所以使用起来基本没什么区别,这里出现了一个新的结构体,pollfd:

struct pollfd

在这里插入图片描述
那么到这里就可以看一下poll是如何解决select的缺点的:

解决缺点的方式

1. 等待的fd是有上限的

在select当中,一个很大的问题是有上限,而这个问题出现的原因是因为select的设计当中,最根源的问题在于它的位图大小决定了只能容纳1024个新的请求,再多了就不能容纳了,所以为了解决这个问题,poll直接设置成无限大小,只要操作系统和内存能放得下,那就能放得下,这是poll对于select的一个解决方案

2. 输入输出型参数比较多

这个问题poll也成功的解决了,poll的解决方案是直接定义一个结构体:

在这里插入图片描述

在这个结构体当中,存在有fd,events,revents,那么这就意味着可以免去拷贝的工作,直接在结构体当中进行设置即可,对于不同类型的参数,用位图来进行表示,存储在short当中:

在这里插入图片描述
在这里插入图片描述
至此,对于输入输出型参数太多导致的问题也被poll解决了

3. 遍历

但是遗憾的是,poll并没有解决遍历带来的效率问题,并且更为严重的是,poll还可以无限的增加数组中元素的个数,所以理论上来说是可以存放100000个数组内容的,这就给遍历带来了相当大的问题,所以遍历这件事poll并没有解决,这是Linux的这种设计模式决定确实不能解决的,具体解决只能留到epoll来解决了

代码实现

整体来说只要会用select,poll就是在它之上进行优化的,所以我只是对于select的代码进行了一些更变就有了poll的代码逻辑

#pragma once

#include <iostream>
#include <poll.h>
#include <sys/time.h>
#include "Socket.hpp"

using namespace std;

static const uint16_t defaultport = 8888;
static const int fd_num_max = 64;
int defaultfd = -1;
int non_event = 0;

class PollServer
{
public:
    PollServer(uint16_t port = defaultport) : _port(port)
    {
        for (int i = 0; i < fd_num_max; i++)
        {
            _event_fds[i].fd = defaultfd;
            _event_fds[i].events = non_event;
            _event_fds[i].revents = non_event;

            // cout << "fd_array[" << i << "]" << " : " << fd_array[i] << endl;
        }
    }
    bool Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        return true;
    }
    void Accepter()
    {
        // 我们的连接事件就绪了
        string clientip;
        uint16_t clientport = 0;
        int sock = _listensock.Accept(&clientip, &clientport); // 会不会阻塞在这里?不会
        if (sock < 0)
            return;
        lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sock);

        // sock -> fd_array[]
        int pos = 1;
        for (; pos < fd_num_max; pos++) // 第二个循环
        {
            if (_event_fds[pos].fd != defaultfd)
                continue;
            else
                break;
        }
        if (pos == fd_num_max)
        {
            lg(Warning, "server is full, close %d now!", sock);
            close(sock);
            // 扩容
        }
        else
        {
            // fd_array[pos] = sock;
            _event_fds[pos].fd = sock;
            _event_fds[pos].events = POLLIN;
            _event_fds[pos].revents = non_event;
            PrintFd();
            // TODO
        }
    }
    void Recver(int fd, int pos)
    {
        // demo
        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)
        {
            lg(Info, "client quit, me too, close fd is : %d", fd);
            close(fd);
            _event_fds[pos].fd = defaultfd; // 这里本质是从select中移除
        }
        else
        {
            lg(Warning, "recv error: fd is : %d", fd);
            close(fd);
            _event_fds[pos].fd = defaultfd; // 这里本质是从select中移除
        }
    }
    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.Fd())
                {
                    Accepter(); // 连接管理器
                }
                else // non listenfd
                {
                    Recver(fd, i);
                }
            }
        }
    }
    void Start()
    {
        _event_fds[0].fd = _listensock.Fd();
        _event_fds[0].events = POLLIN;
        int timeout = 3000; // 3s
        for (;;)
        {
            int n = poll(_event_fds, fd_num_max, timeout);
            switch (n)
            {
            case 0:
                cout << "time out... " << endl;
                break;
            case -1:
                cerr << "poll error" << endl;
                break;
            default:
                // 有事件就绪了,TODO
                cout << "get a new link!!!!!" << endl;
                Dispatcher(); // 就绪的事件和fd你怎么知道只有一个呢???
                break;
            }
        }
    }
    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()
    {
        _listensock.Close();
    }

private:
    Sock _listensock;
    uint16_t _port;
    struct pollfd _event_fds[fd_num_max]; // 数组, 用户维护的!
    // struct pollfd *_event_fds;

    // int fd_array[fd_num_max];
    // int wfd_array[fd_num_max];
};

而在实际的使用当中,除非是在特殊的环境下,比如是比较老的平台,否则一般不用select,还是用后面新出的这些接口比较好用

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐