采用默认高电平触发(水平触发)同步非阻塞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;
}