Skip to content

guangqianpeng/tinyev

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

93 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tinyev: A multithreaded C++ network library

Build Status

简介

tinyev是仿照muduo[1]实现的一个基于Reactor模式的多线程C++网络库,经过适当简化以后代码量约为2000行。简化的部分如下:

  • 多线程依赖于C++11提供的std::thread库,而不是重新封装POSIX thread API。

  • 定时器依赖于C++11提供的std::chrono库,而不是自己实现Timstamp类,也不用直接调用gettimeofday()。这样写的好处之一是我们不必再为定时器API的时间单位操心[2]:

    using namespace std::literals::chrono_literals;
    loop.runEvery(10s, [](){INFO("run every 10 seconds");});
    loop.runAfter(24h, [](){INFO("run after 24 hours");});
    loop.runAt(Clock::nowAfter(15min), [](){INFO("run 15 minutes later");});
  • 默认为accept socket开启SO_RESUEPORT选项,这样每个线程都有自己的Acceptor,就不必在线程间传递connection socket。开启该选项后内核会将TCP连接分摊给各个线程,因此不必担心负载均衡的问题。

  • 仅具有简单的日志输出功能,用于调试。

  • 仅使用epoll,不使用poll和select。

示例

一个简单的echo服务器如下:

class EchoServer: noncopyable
{
public:
  EchoServer(EventLoop* loop, const InetAddress& addr)
    : loop_(loop),
      server_(loop, addr),
  {
    // set echo callback
    server_.setMessageCallback(std::bind(&EchoServer::onMessage, this, _1, _2));
  }
  void start() { server_.start(); }
  void onMessage(const TcpConnectionPtr& conn, Buffer& buffer)
  {
    // echo message
    conn->send(buffer);
  }
private:
  EventLoop* loop_;
  TcpServer server_;
};

这个实现非常简单,读者只需关注onMessage回调函数,它将收到消息发回客户端。然而,该实现有一个问题:若客户端只发送而不接收数据(即只调用write而不调用read),则TCP的流量控制(flow control)会导致数据堆积在服务端,最终会耗尽服务端内存。为解决该问题我们引入高/低水位回调:

class EchoServer: noncopyable
{
public:
  ...
  void onConnection(const TcpConnectionPtr& conn)
  {
    if (conn->connected())
      conn->setHighWaterMarkCallback(
            std::bind(&EchoServer::onHighWaterMark, this, _1, _2), 1024);
  }
  void onHighWaterMark(const TcpConnectionPtr& conn, size_t mark)
  {
    INFO("high water mark %lu bytes, stop read", mark);
    conn->stopRead();
  }
  void onWriteComplete(const TcpConnectionPtr& conn)
  {
    if (!conn->isReading()) {
      INFO("write complete, start read");
      conn->startRead();
    }
  }
  ...
};

我们新增了3个回调:onConnectiononHighWaterMarkonWriteComplete。当TCP连接建立时onConnection会设置高水位回调值(high water mark);当send buffer达到该值时,onHighWaterMark会停止读socket;当send buffer全部写入内核时,onWriteComplete会重新开始读socket。

除此以外,我们还需要给服务器加上定时功能以清除空闲连接。实现思路是让服务器保存一个TCP连接的std::map,每隔几秒扫描一遍所有连接并清除超时的连接,代码在这里

然后,我们给服务器加上多线程功能。实现起来非常简单,只需加一行代码即可:

EchoServer::void start()
{
  // set thread num here
  server_.setNumThread(2);
  server_.start();
}

最后,main函数如下:

int main()
{
  EventLoop loop;
  // listen address localhost:9877
  InetAddress addr(9877);
  // echo server with 4 threads and timeout of 10 seconds
  EchoServer server(&loop, addr, 4, 10s);
  // loop all other threads except this one
  server.start();
  // quit after 1 minute
  loop.runAfter(1min, [&](){ loop.quit(); });
  // loop main thread
  loop.loop();
}

安装

$ git clone [email protected]:guangqianpeng/tinyev.git
$ cd tinyev
$ git submodule update --init --recursive
$ ./build.sh 
$ ./build.sh install

tinyev安装在 ../tinyev-build/Release/{include, lib}

更多

网络库的具体实现方法参考我的博客

参考

[1] Muduo is a multithreaded C++ network library based on the reactor pattern.

[2] CppCon 2017: Bjarne Stroustrup “Learning and Teaching Modern C++”. Make interfaces precisely and strongly typed.

Releases

No releases published

Packages

No packages published