I/O多路转接之poll(实现poll版本的TCP服务器)

poll函数接口

#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).
  • events和revents的取值具体我们可以参考man手册

返回结果

  • 返回值小于0, 表示出错;
  • 返回值等于0, 表示poll函数等待超时;
  • 返回值大于0, 表示poll由于监听的文件描述符就绪而返回.

poll的优点

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

poll的缺点

  • poll中监听的文件描述符数目增多时和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符.
  • 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中.
  • 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降.

poll版本的TCP服务器代码

完整代码请前往我的GitHub仓库—>>点我哇

#pragma once
#include"tcp_socket.hpp"
#include<poll.h>
#include<unistd.h>
#include<cstring>
#include<vector>

class Poll{
  public:
    Poll()
      :fds_(nullptr)
       ,fds_size_(15)
       ,nfds_(0)
       ,timeout_(1000)
  {
    fds_=new pollfd[fds_size_];
  }
    bool Add(const 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(const TcpSocket& sock){
      int del_fd=sock.GetFd();
      for(size_t i=0;i<nfds_;++i){
        if(fds_[i].fd==del_fd){
          fds_[i]=fds_[--nfds_];
          break;
        }
      }
      return true;
    }
    bool Wait(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){
        cout<<"poll wait timeout"<<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 void (*Handler)(const string& buf,string& ret);

class TcpPollServer{
  public:
    TcpPollServer()
    {}
    ~TcpPollServer(){
      _sock.Close();
    }
    bool Start(const string& ip,const uint16_t port,Handler handler){
      if(_sock.Socket()==false)
        return false;
      if(_sock.Bind(ip,port)==false)
        return false;
      if(_sock.Listen()==false)
        return false;
      
      Poll poll;
      poll.Add(_sock);

      while(1){
        vector<TcpSocket> list;
        if(poll.Wait(list)==false)
          continue;
        for(size_t i=0;i<list.size();++i){
          if(list[i].GetFd()==_sock.GetFd()){
            TcpSocket NewSock;
            string ClientIp;
            uint16_t ClientPort;
            if(_sock.Accept(&NewSock,ClientIp,ClientPort)==false)
              continue;
            printf("[%s:%d]客户端已连接!~\n",ClientIp.c_str(),ClientPort);
            poll.Add(NewSock);
          }
          else{
            string msg;
            int n=list[i].Recv(msg);
            if(n==0){
              poll.Del(list[i]);
              list[i].Close();
              continue;
            }else{
              cout<<"客户端发送:"<<msg<<endl;
              string ret;
              handler(msg,ret);
              list[i].Send(ret);
            }
          }
        }
      }
      return true;
    }
  private:
    TcpSocket _sock;
};
发布了161 篇原创文章 · 获赞 52 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_42837885/article/details/103651641