epoll reactor反应堆服务器demo学习

采用默认高电平触发(水平触发)同步非阻塞I/O
简单模仿陈硕的muduo,实现一个高吞吐的服务器模型框架
使用多态代替muduo::net::Channel和std::bind接口
提供异步定时器,接受任意类型调用对象,返回一个future
已上传github作为学习使用 github_link

//copyablility.hh  提供对象的拷贝权限
#ifndef __COPYABILITY__
#define __COPYABILITY__

class copyable {
 protected:
  copyable() = default;
  ~copyable() = default;
};

class noncopyable {
 public:
  noncopyable(const noncopyable &) = delete;
  void operator=(const noncopyable &) = delete;
  noncopyable(noncopyable &&) noexcept = delete;
  void operator=(noncopyable &&) noexcept = delete;

 protected:
  noncopyable() = default;
  ~noncopyable() = default;
};

#endif
//i_object.hh 抽象类 作为事件回调的对象
#ifndef __INTERFACE_OBJECT__
#define __INTERFACE_OBJECT__

class Object {
 public:
  virtual ~Object() = default;
  virtual void operator()(int events) final;
  virtual void handleRead() = 0;
  virtual void handleWrite() = 0;
};

#endif
#include "i_object.hh"

#include <sys/epoll.h>

void Object::operator()(int events) {
  if (events & (EPOLLIN | EPOLLPRI)) {
    handleRead();
    return;
  }

  if (events & EPOLLOUT) {
    handleWrite();
  }
}
// epoller.hh
// epoller.hh
#ifndef __EPOLLER__
#define __EPOLLER__

#include <sys/epoll.h>
#include <sys/types.h>
#include <unistd.h>

#include <functional>
#include <future>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <typeinfo>
#include <vector>

#include "copyability.hh"
#include "i_object.hh"
#include "timer.hh"

class Timer;
class Epoller : noncopyable {
 public:
  Epoller();
  void loop();
  void quit();

  void addEvent(int fd, int operartion, Object* callback);
  void removeEvent(int fd, Object* callback);

  // async call. return a future.
  template <class F, class... Args>
  auto runAfter(int second, F&& _func, Args&&... _args)
      -> std::future<decltype(_func(_args...))> {
    using rtype = decltype(_func(_args...));

    auto task = std::make_shared<std::packaged_task<rtype()>>(
        std::bind(std::forward<F>(_func), std::forward<Args>(_args)...));

    std::future<rtype> res = task->get_future();

    {
      if (timerTie_) {
        timerTie_->setTimer(second, [task]() { (*task)(); });
      }
    }

    return res;
  }

 private:
  static const int kTimeoutMs_ = 1000;
  bool quit_;
  int efd_;
  std::unique_ptr<Timer> timerTie_;
  std::vector<struct epoll_event> activeEvents_;
};

#endif
//  epoller.cc
#include "epoller.hh"

#include <arpa/inet.h>
#include <assert.h>

#include <cstring>
#include <functional>
#include <iostream>

Epoller::Epoller() : quit_(false), efd_{-1}, timerTie_{}, activeEvents_(10) {
  efd_ = ::epoll_create(10);
  assert(efd_ >= 0);

  timerTie_.reset(new Timer(this));
  std::cout << "timerTie_:" << static_cast<void*>(timerTie_.get()) << std::endl;
}

void Epoller::addEvent(int fd, int operartion, Object* callback) {
  assert(callback != nullptr);

  struct epoll_event ev;
  ::memset(&ev, 0, sizeof(struct epoll_event));
  ev.events = operartion;
  ev.data.fd = fd;
  ev.data.ptr = callback;

  assert(::epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &ev) >= 0);
  std::cout << "add clt:" << fd << " to event_loop." << std::endl;
}

void Epoller::removeEvent(int fd, Object* callback) {
  // 从红黑树上摘下
  struct epoll_event event;
  ::memset(&event, 0, sizeof event);

  assert(::epoll_ctl(efd_, EPOLL_CTL_DEL, fd, &event) >= 0);
  std::cout << "delete clt:" << fd << " callback:" << event.data.ptr
            << " from event_loop." << std::endl;
}

void Epoller::loop() {
  while (!quit_) {
    int ready = ::epoll_wait(efd_, &*activeEvents_.begin(),
                             activeEvents_.size(), kTimeoutMs_);

    if (1 <= ready) {
      for (size_t i = 0; i != ready; ++i) {
        if (nullptr != activeEvents_[i].data.ptr) {
          auto cb = static_cast<Object*>(activeEvents_[i].data.ptr);
          (*cb)(activeEvents_[i].events);
        }
      }

      if (ready == activeEvents_.size()) {
        activeEvents_.resize(activeEvents_.size() * 2);
      }
    }
  }
}

void Epoller::quit() { quit_ = true; }

//connector.hh server每建立一个连接就创建一个connector对象管理链接
//会被注册到事件循环
//有缺陷,不想改,反正是demo
#ifndef __CONNECTOR__
#define __CONNECTOR__

#include <memory>
#include <string>

#include "copyability.hh"
#include "epoller.hh"
#include "i_object.hh"
#include "server.hh"

class Server;

class Connector : noncopyable, public Object {
 public:
  explicit Connector(int fd, Epoller* poller, Server* server);
  ~Connector() override;
  
  void handleRead() override;
  void handleWrite() override;

  int cltFd() const;
  void set_cltFd(int fd);

  struct Message {
    unsigned char data[30];
  };

 private:
  static const uint8_t bufferSz_ = 100;
  int cltFd_;
  std::unique_ptr<unsigned char[]> buffer_;
  Epoller* poller_;
  Server* server_;
};

#endif
//connector.cc
#include "connector.hh"

#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>
#include <cstring>
#include <iostream>

Connector::Connector(int fd, Epoller* poller, Server* server)
    : cltFd_(fd),
      buffer_{new unsigned char[bufferSz_]{}},
      poller_(poller),
      server_(server) {
  assert(poller != nullptr);
  assert(server_ != nullptr);

  int opt = ::fcntl(cltFd_, F_GETFL, 0);
  ::fcntl(cltFd_, F_SETFL, opt | O_NONBLOCK);
}

Connector::~Connector() {}

int Connector::cltFd() const { return cltFd_; }

void Connector::set_cltFd(int fd) { cltFd_ = fd; }

void Connector::handleRead() {
  {
    ::memset(buffer_.get(), 0, bufferSz_);

    while (true) {
      int ret = ::read(cltFd_, buffer_.get(), sizeof(Message));

      if (ret < 0) {
        if (errno == EWOULDBLOCK or errno == EINTR) {
          std::cout << "read again plz.\n";
          continue;
        }
      }

      if (0 == ret) {
        assert(poller_ != nullptr);
        assert(server_ != nullptr);

        poller_->removeEvent(cltFd_, this);

        // TODO: 移除服务器中维护的客户端连接
        server_->removeClt(cltFd_);
        break;
      }

      static int loop = 0;
      std::cout << "loop" << ++loop << " recv msg:["
                << std::string((char*)buffer_.get(), ret - 1)
                << "] from:" << cltFd_ << std::endl;
      break;
    }
  }
}

void Connector::handleWrite() { std::cout << "TODO: \n"; }
//server.hh
//会被注册到事件循环
#ifndef __SERVER__
#define __SERVER__

#include <arpa/inet.h>  //sockaddr_in

#include <cstring>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "connector.hh"
#include "copyability.hh"
#include "epoller.hh"
#include "i_object.hh"

class Connector;

class Server : noncopyable, public Object {
 public:
  explicit Server(uint16_t listenPort, const char* ip, Epoller* poller);
  ~Server() override;
  void start();

  void handleRead() override;
  void handleWrite() override;

  void accept();
  int listenfd() const;
  Epoller* poller() { return poller_; }
  void addClt(Connector* clt);
  void addClt(int cltfd) = delete;
  void removeClt(int cltfd);

 private:
  int listenfd_;
  Epoller* poller_;
  sockaddr_in sock_;
  using ClientList = std::vector<Connector*>;
  ClientList cltList_;
};

#endif
// server.cc
#include "server.hh"

#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>

Server::Server(uint16_t listenPort, const char* ip, Epoller* poller)
    : listenfd_{}, poller_(poller), sock_{}, cltList_{} {
  assert(ip != nullptr);
  assert(poller_ != nullptr);

  sock_.sin_family = AF_INET;
  sock_.sin_port = htons(listenPort);
  sock_.sin_addr.s_addr = ::inet_addr(ip);

  listenfd_ = ::socket(AF_INET, SOCK_STREAM, 0);
  assert(listenfd_ != -1);

  int optval = 1;
  ::setsockopt(listenfd_, SOL_SOCKET, SO_REUSEADDR, &optval,
               static_cast<socklen_t>(sizeof optval));

  int opt = ::fcntl(listenfd_, F_GETFL, 0);
  ::fcntl(listenfd_, F_SETFL, opt | O_NONBLOCK);

  assert(::bind(listenfd_, reinterpret_cast<sockaddr*>(std::addressof(sock_)),
                sizeof(sockaddr_in)) >= 0);

  assert(::listen(listenfd_, listenPort) >= 0);
}

Server::~Server() {
  if (!cltList_.empty()) {
    for (auto& clt : cltList_) {
      if (clt->cltFd() != -1) {
        ::close(clt->cltFd());
      }
    }
  }

  ::close(listenfd_);
}

void Server::start() { poller_->addEvent(listenfd_, EPOLLIN | EPOLLPRI, this); }

void Server::accept() {
  struct sockaddr_in clientInfo {};
  int size = sizeof clientInfo;

  // 从accept队列中pop一个建立好连接的fd出来
  int cltFd = ::accept(listenfd_, (sockaddr*)&clientInfo, (socklen_t*)&size);
  assert(-1 != cltFd);

  Connector* newConnect = new Connector(cltFd, poller_, this);
  addClt(newConnect);

  poller_->addEvent(cltFd, EPOLLIN | EPOLLPRI, newConnect);
}

int Server::listenfd() const { return listenfd_; }

void Server::addClt(Connector* clt) { cltList_.emplace_back(clt); }

void Server::removeClt(int cltfd) {
  for (auto it = cltList_.begin(); it != cltList_.end(); ++it) {
    ::close(cltfd);

    if (cltfd == (*it)->cltFd()) {
      auto clt = *it;
      delete clt;

      cltList_.erase(it);
      break;
    }
  }
}

void Server::handleRead() {
  std::cout << "register client\n";
  accept();
}

void Server::handleWrite() { std::cout << "TODO: \n"; }
// timer.hh 定时器,时间到了出发写事件
#ifndef __TIMER__
#define __TIMER__

#include <functional>

#include "copyability.hh"
#include "epoller.hh"
#include "i_object.hh"

class Epoller;
class Timer : noncopyable, public Object {
 public:
  explicit Timer(int second, std::function<void()> callback, Epoller *poller);
  explicit Timer(Epoller *poller);
  ~Timer() override;

  void handleRead() override;
  void handleWrite() override;

  void go();
  int timerfd() const { return timefd_; }

  void setTimer(int second, std::function<void()> callback, bool go = true);

 private:
  int timefd_;
  Epoller *poller_;
  struct itimerspec howlong_;
  std::function<void()> callback_;
};

#endif
#include "timer.hh"

#include <assert.h>
#include <sys/timerfd.h>

#include <cstring>

Timer::Timer(Epoller *poller)
	// TFD_CLOEXEC 在fork时关闭改fd,禁止子进程共享fd
    : timefd_{::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)},
      poller_(poller),
      howlong_{},
      callback_{} {}

Timer::Timer(int second, std::function<void()> callback, Epoller *poller)
    : timefd_(::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)),
      poller_(poller),
      howlong_{},
      callback_(callback) {
  ::memset(&howlong_, 0, sizeof(itimerspec));
  howlong_.it_value.tv_sec = second;
  ::timerfd_settime(timefd_, 0, &howlong_, nullptr);
}

Timer::~Timer() {
  if (timefd_ > 0) {
    ::close(timefd_);
  }
}

void Timer::setTimer(int second, std::function<void()> callback,
                     bool rightNow) {
  callback_ = callback;

  ::memset(&howlong_, 0, sizeof(itimerspec));
  howlong_.it_value.tv_sec = second;

  if (rightNow) {
    ::timerfd_settime(timefd_, 0, &howlong_, nullptr);
    go();
  }
}

void Timer::handleRead() {
  uint64_t buf;
  ::read(timerfd(), &buf, sizeof(uint64_t));

  if (callback_) {
    callback_();
  }
}

void Timer::handleWrite() {}

void Timer::go() { poller_->addEvent(timerfd(), EPOLLIN | EPOLLOUT, this); }

cmake_minimum_required(VERSION 3.5)

project(SERVER)

aux_source_directory(${PROJECT_SOURCE_DIR} DIR_MAIN_SRCS)

# -I

# parameterss
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAG ${CMAKE_CXX_FLAG} "-g -Wall")

# macro

# -L

# -o
add_executable(reactor_server 
    ${DIR_MAIN_SRCS} 
	)

# -l
set(LIB_NAME 
    pthread
    )
target_link_libraries(reactor_server ${LIB_NAME})

#include "server.hh"
#include <thread>
// #include "timer.hh"

constexpr uint16_t g_port = 6666;
const char* g_ip = "127.0.0.1";

auto main() -> int {
  Epoller eventloop{};

  Server server{g_port, g_ip, std::addressof(eventloop)};
  server.start();

  // 单独使用定时器
  // Timer timer(
  //     10, [&] { eventloop.quit(); }, std::addressof(eventloop));
  // timer.go();

  // 使用Epoller内部定时器,当前线程
  // eventloop.runAfter(10, [&] { eventloop.quit(); });
  
  // 使用Epoller内部定时器,多线程
  std::thread t([&] { eventloop.runAfter(10, [&] { eventloop.quit(); }); });
  t.join();

  eventloop.loop();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值