标签搜索

目 录CONTENT

文章目录

IO多路转接之poll.md

小小城
2021-08-22 / 0 评论 / 0 点赞 / 5 阅读 / 3,418 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

IO多路转接之poll

@[toc]

一、Poll

  •  1.函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数作用
struct pollfd *fdsfds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合
nfds_t nfdsnfds表示fds数组的长度
timeouttimeout表示poll函数的超时时间, 单位是毫秒(ms)

返回值

  • 返回值小于0, 表示出错;
  • 返回值等于0, 表示poll函数等待超时;
  • 返回值大于0, 表示poll由于监听的文件描述符就绪而返回
  •  2.理解struct pollfd *fds
// pollfd结构
struct pollfd {
	int fd; /* file descriptor */
	short events; /* requested events */
	short revents; /* returned events */
};

其中enent和revent的取值(主要取值),不清楚可以找Man
|取值|描述 |
|--|--|
| POLLIN| 数据可读(普通数据和优先级数据)
|POLLOUT|数据可写(普通数据和优先级数据)
|POLLERR|错误

  •  3.socket就绪条件

  • 读就绪

  • sochet内核中,接收缓冲区中的字节数,大于等于低水位标记SO_RECVLOWAT,此时可以无阻塞的读该文件描述符,并且返回值大于0

  • socket TCP通信中,对端关闭连接,此时对该socket读,则返回0 监听的socket上有新的连接请求时

  • socket上有未处理的错误时

  • 写就绪

  • socket内核中,发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小),大于等于低水位标记,SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0

  • socket的写操作被关闭(close或者shutdown),对于一个写操作被关闭的socket进行写操作,会触发SIGPIPE信号

  • socket使用非阻塞connect连接成功或失败之后 socket上有未读取的错误

  • 异常就绪

  • socket上收到带外数据(关于带外数据,和TCP紧急模式相关,在TCP报头中有一个紧急指针的字段)

  •  4.poll的优点及缺点

poll的优点

  • 不同与select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现.pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比 select更方便.
  • poll并没有最大数量限制 (但是数量过大后性能也是会下降).

poll的缺点

  • poll中监听的文件描述符数目增多时 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
  • 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中. 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长, 其效 率也会线性下降
  •  5.基于poll实现tcp服务器(多用户)
#pragma once
#include <functional>
#include "tcp_socket.hpp"
#include <poll.h>
#include <unistd.h>
#include <cstring>
#include <vector>
#include <string>
#include <iostream>

class Poll
{
  public:
    Poll()
      :fds_(nullptr)
       ,fds_size_(15)
       ,nfds_(0)
       ,timeout_(1000)
  {
    fds_=new pollfd[fds_size_];

  }

    bool Add(TcpSocket& sock)
    {
      if(nfds_== fds_size_)
      {//需要扩容
        pollfd* new_fds = new pollfd[2 * fds_size_];

        memcpy(new_fds,fds_,fds_size_ * sizeof(pollfd));
        fds_size_ *= 2;

        delete[] fds_;
        fds_ = new_fds;
      }

      fds_[nfds_].fd = sock.GetFd();
      fds_[nfds_].events = POLLIN;
      ++nfds_;
      return true;
    }

    bool Del(TcpSocket& sock)
    {
      int del_fd = sock.GetFd();

      for(size_t i = 0;i < nfds_;++i)
      {//在fds_内才移除
        if(fds_[i].fd == del_fd)
        {
          fds_[i] = fds_[--nfds_];
          break;
        }
      }
      return true;

    }

    bool Wait(std::vector<TcpSocket>& list,int timeout = 1000)
    {
      timeout_=timeout;

      int ret = poll(fds_,nfds_,timeout_);
      if(ret < 0)
      {
        perror("Poll Wait");
        return false;

      }
      else if(ret == 0)
      {
        std::cout<<"poll wait timeout"<<std::endl;
        return false;

      }

      for(nfds_t i = 0;i < nfds_;++i)
      {
        if(fds_[i].revents == POLLIN)
        {
          TcpSocket sock(fds_[i].fd);
          list.push_back(sock);
        }

      }
      return true;
    }

    int GetNfds()
    {
      return nfds_;

    }

    const pollfd* GetFds()
    {
      return fds_;
    }

  private:
    pollfd* fds_;
    nfds_t fds_size_;
    nfds_t nfds_;
    int timeout_;
};


typedef std::function<void(const std::string& req,std::string* resp)> Handler;

class TcpPollServer
{
  public:
    TcpPollServer(const std::string& ip,const uint16_t port)
      :ip(ip)
       ,port(port)
    {}

    ~TcpPollServer()
    {
      _sock.Close();

    }

    bool Start(Handler handler){

      if(!_sock.Socket())
        return false;

      if(!_sock.Bind(ip,port))
        return false;

      if(!_sock.Listen())
        return false;

      Poll poll;
      poll.Add(_sock);

      while(1)
      {
        std::vector<TcpSocket> list;

        if(!poll.Wait(list))
          continue;

        for(size_t i = 0;i < list.size();++i)
        {
          if(list[i].GetFd() == _sock.GetFd())
          {
            TcpSocket NewSock;
            if(!_sock.Accept(&NewSock,NULL,NULL))
              continue;

            printf("客户端已连接!~\n");
            poll.Add(NewSock);
          }
          else
          {
            std::string req;
            int n = list[i].Recv(&req);
            if(n == 0)
            {
              poll.Del(list[i]);
              list[i].Close();
              continue;

            }
            else
            {
             std::cout<<"客户端发送:"<<req<<std::endl;
              std::string resp;
              handler(req,&resp);
              list[i].Send(resp);
            }
          }
        }
      }
      return true;
    }
  private:
    TcpSocket _sock;
    std::string ip;
    uint16_t port;
};

在这里插入图片描述

在这里插入图片描述

0

评论区