Caturra000 / mutty

yet another network library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Mutty

TL;DR

Mutty是一个基于C++14Reactor模型开发的基础网络库,满足Modern C++和高性能的需求,拥有简洁的接口,适用于TCP通信,并且header-only

快速使用

这是一个echo server/client的demo,可以了解到mutty的大概使用流程

#include <bits/stdc++.h>
#include "mutty.hpp"
using namespace mutty;

template <typename T>
void print(T &&message, std::ostream &o = std::cout) { o << message << std::endl; }

int main() {
    Looper looper;
    Server server(&looper, {"127.0.0.1", 2333});

    server.onConnect(::puts, "connection created.");

    server.onMessage([](TcpContext *ctx) {
        auto &buf = ctx->inputBuffer;
        print(buf);
        ctx->send(buf.readBuffer(), buf.unread());
        buf.read(buf.unread());
    });

    server.onClose([] { print("connection close."); });
    
    server.start();
    looper.loop();
    return 0;
}
#include <bits/stdc++.h>
#include "mutty.hpp"
using namespace mutty;

template <typename T>
void print(T &&message, std::ostream &o = std::cout) { o << message << std::endl; }

int main() {
    ClientLooper clooper;
    Client client(clooper, {"127.0.0.1", 2333});
    std::weak_ptr<TcpContext> chat; // safe
    client.onConnect([&](std::weak_ptr<TcpContext> ctx) {
        chat = ctx;
        print("[client] connected.");
    });
    client.onClose([&] {
        print("[client] closed.");
        auto looper = clooper.get();
        looper->stop();
    });
    client.connect(); // async
    for(std::string s; std::getline(std::cin, s);) {
        if(auto ctx = chat.lock()) {
            ctx->send(s);
            print("[client] result has been sent to buffer.");
        } else {
            print("[client] send failed. check connection status.", std::cerr);
        }
    }
    return 0;
}

文件分布

src包含所有实现代码,介绍会以bottom-up的形式进行

utils是一个基础库,实现了如下的功能模块,可独立于mutty项目使用:

  • Callable:可调用对象的抽象,也是mutty中所有callback类型的实现
  • Defer:实现类似于GO语言中的defer语义
  • Timestamp:提高时间接口舒适度的类型别名,可以实现1s / 5min / 12ns的表示形式
  • Object / Exchanger:一个std::anycopyable / noncopyable简化版
  • ThreadPool:提供简单的通用线程池
  • Algorithms / StringUtils:实现一些二进制操作、随机数生成、字符串分割之类的便利功能
  • Pointer:实现一个支持move语义的原生指针封装
  • Compat:对于C++11标准缺失的接口会在这里以namespace cpp11提供
  • TypeTraits:类型萃取的封装,主要给SFINAE提供支持

net是关于socket的一些最简单的封装,原则是与unix api兼容:

  • Socketsocket文件描述符的简单抽象,只负责管理生命周期
  • InetAddresssockaddr族的简单封装,方便快速构造和处理大小端问题

basemutty项目底层模块的划分

  • handler/:实现Message-MessageQueue-Handler的派发机制
  • buffer/:提供可缓存、可减少内存碎片的Buffer实现
  • timer/:一个通用、简洁的定时器,基于堆的实现
  • context/:负责资源的上下文管理,也处理IO多路复用的交互,可暴露于回调接口

core是涉及Reactor的实现模块,依赖于base模块

  • AcceptHandler / AcceptContextaccept过程及其回调接口的封装
  • TcpHandler / TcpContext / ConnectionPoolTcp连接的处理、上下文、全局管理的抽象
  • Looper / LooperPoolReactor的入口
  • Multiplexer:IO多路复用实现的模块,只提供epoll封装

app是直接使用的模块

  • Server
  • Client

实现细节

mutty的实现是极其轻量级的,但不意味着写的随便

接口提供

对外接口的提供为了尽可能简洁,使用了诸如Builderuser-define literalSFINAEPolicy的技巧

对于serverclient,扩展功能并不需要继承一个类来重载实现,可以在对象的层面上直接注册回调,并提供多种选择

一个server或者client提供以下基本回调接口

server.onConnect();
server.onMessage();
server.onWriteComplete();
server.onClose();

client为例,每种回调注册都可以接受无状态、有上下文、含生命周期管理的上下文三种回调接口

一般配合lambda的捕获,功能足够强大

#include <bits/stdc++.h>
#include "app/Client.h"

void print(const char *name, int num, double rate) {
    std::cout << name << " " << num << " " << rate << std::endl;
}

int main() {
    Looper looper;
    Client client(&looper, InetAddress("127.0.0.1", 2333));
    // 无状态
    client.onConnect(print, "jojo", 1, 0.3);
    client.onConnect([] { abort(); });
    // 上下文
    client.onConnect([&](TcpContext *ctx) {
        // ...
    });
    // 生命周期安全
    client.onConnect([](std::weak_ptr<TcpContext> context) {
        if(auto ctx = context.lock()) {
            // ...
        }
    });
    
    client.start();
    looper.loop();
}

另外,定时器的使用也是极其轻松的

TcpContext *ctx; 
// ...
timer.runAfter(now() + 5s).atMost(3).per(500ms)
    .with([ctx] { ctx->send("tick-tock"); });

内部接口为了实现上的舒适也造了少量轮子,如CallableDefer

线程安全

线程安全参考了muduorunInLoop做法,但是这里并不是通过一个封装std::queue<std::function>的容器来实现

mutty的线程安全分为两个方法,

一种是handler机制,用于固定发生的事件回调,

通过Message生成消息,放入MessageQueue派发到合适的Handler来实现调度到Loop线程

另一种是直接基于Timer,通过Timer::ResultSetLoop线程获取合适的事件

这两种都用到swap的技巧来减小锁的粒度

缓存和碎片处理

// 有空写

TODO

  • LOG
  • test
  • Codec-demo

MORE

十行以内实现一个defer

通过滑动窗口来优化vector

设计一个引起舒适的回调接口

定时器的简单讨论

About

yet another network library


Languages

Language:C++ 99.5%Language:C 0.5%