Mutty
是一个基于C++14
和Reactor
模型开发的基础网络库,满足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::any
的copyable / noncopyable
简化版ThreadPool
:提供简单的通用线程池Algorithms / StringUtils
:实现一些二进制操作、随机数生成、字符串分割之类的便利功能Pointer
:实现一个支持move
语义的原生指针封装Compat
:对于C++11
标准缺失的接口会在这里以namespace cpp11
提供TypeTraits
:类型萃取的封装,主要给SFINAE
提供支持
net
是关于socket
的一些最简单的封装,原则是与unix api
兼容:
Socket
:socket
文件描述符的简单抽象,只负责管理生命周期InetAddress
:sockaddr
族的简单封装,方便快速构造和处理大小端问题
base
是mutty
项目底层模块的划分
handler/
:实现Message-MessageQueue-Handler
的派发机制buffer/
:提供可缓存、可减少内存碎片的Buffer
实现timer/
:一个通用、简洁的定时器,基于堆的实现context/
:负责资源的上下文管理,也处理IO多路复用的交互,可暴露于回调接口
core
是涉及Reactor
的实现模块,依赖于base
模块
AcceptHandler / AcceptContext
:accept
过程及其回调接口的封装TcpHandler / TcpContext / ConnectionPool
:Tcp
连接的处理、上下文、全局管理的抽象Looper / LooperPool
:Reactor
的入口Multiplexer
:IO多路复用实现的模块,只提供epoll
封装
app
是直接使用的模块
Server
Client
mutty
的实现是极其轻量级的,但不意味着写的随便
对外接口的提供为了尽可能简洁,使用了诸如Builder
、user-define literal
、SFINAE
、Policy
的技巧
对于server
和client
,扩展功能并不需要继承一个类来重载实现,可以在对象的层面上直接注册回调,并提供多种选择
一个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"); });
内部接口为了实现上的舒适也造了少量轮子,如Callable
和Defer
线程安全参考了muduo
的runInLoop
做法,但是这里并不是通过一个封装std::queue<std::function>
的容器来实现
mutty
的线程安全分为两个方法,
一种是handler
机制,用于固定发生的事件回调,
通过Message
生成消息,放入MessageQueue
派发到合适的Handler
来实现调度到Loop
线程
另一种是直接基于Timer
,通过Timer::ResultSet
在Loop
线程获取合适的事件
这两种都用到swap
的技巧来减小锁的粒度
// 有空写
- LOG
- test
- Codec-demo