muduo

Muduo网络库源码分析之定时器的实现

南楼画角 提交于 2020-12-07 20:52:37
muduo 的定时器功能由三个 class 实现,TimerId、Timer 和 TimerQueue。 TimerId 类 它唯一标识一个 Timer 定时器。TimerId Class 同时保存Timer* 和 sequence_,这个 sequence_ 是每个 Timer 对象有一个全局递增的序列号 int64_t sequence_,用原子计数器(AtomicInt64)生成。 它主要用于注销定时器,这样就可以区分地址相同的先后两个 Timer 对象。 namespace muduo { namespace net { class Timer; /// /// An opaque identifier, for canceling Timer. /// /* 带有唯一标识的Timer,主要用于取消Timer */ class TimerId : public muduo::copyable { public : TimerId () : timer_ (NULL), sequence_ (0) { } TimerId(Timer* timer, int64_t seq) : timer_(timer), //timer 定时器的指针 sequence_(seq) //seq 该定时任务的序列号 { } // default copy-ctor, dtor and

Muduo网络库源码分析之Acceptor和TcpServer

北城以北 提交于 2020-12-07 20:51:34
Acceptor 用于 accept 一个 TCP 连接,accept 接受成功后通知 TCP 连接的使用者。Acceptor 主要是供 TcpServer 使用的,其生命期由后者控制。一个 Acceptor 相当于持有服务端的一个 socket 描述符,该 socket 可以 accept 多个 TCP 客户连接,这个 accept 操作就是 Acceptor 实现的。 这里用到了一些封装好的 socket 和地址结构,如 class InetAddress 表示 sockaddr_in 的封装,如可以通过ip地址和port端口生成一个sockaddr_in; class Socket封装了部分关于socket套接字的操作,如 Socket::bindAddress(InetAddress&) 将socket和一个sockaddr_in地址绑定, Socket::accept(InetAddress& peerAddr) 将一个socket允许连接一个客户端地址peerAddr, Socket::listen() 监听socket, Socket::shutdownWrite() 实现关闭socket的写。这些类的封装可以看我这篇博客的分析。 Muduo网络库源码分析之对socket及其相关操作的封装 Acceptor在构造的时候会创建一个 socket 描述符

Muduo网络库源码分析之TcpConnection Class

拟墨画扇 提交于 2020-12-07 20:10:01
用于管理一个具体的 TCP 连接,比如消息的接收与发送,完成用户指定的连接回调 connectionCallback 。 TcpConnection 构造时接收参数有 TCP 连接的 sockfd,服务端地址 localAddr ,客户端地址 peerAddr ,并通过 Socket 封装 sockfd 。并用 Channel 管理该 sockfd,向 Channel 注册可读、可写、关闭、出错回调函数,用于 Poller 返回就绪事件后 Channel::handleEvent() 执行相应事件的回调。 TcpConnection 有四个状态,简单的状态图: TcpConnection 有一系列用户指定的事件回调函数,比如 TcpConnection::connectionCallback 、 messageCallback 、 writeCompleteCallback ,这些是用户通过 TcpServer 传给 TcpConnection。当 Poller 返回 TcpConnection 对应的 Socket 就绪事件时, Channel::handleEvent() -> TcpConnection::handle*系列函数 -> 这些事件回调函数(如 connectionCallback)。 与上面对应,TcpConnection 有一系列 用于 TcpServer

2.muduo之Channel

£可爱£侵袭症+ 提交于 2020-09-29 16:56:12
Channel类主要作用:1.将文件描述符(可能是socket类型,eventfd类型,timefd类型,signalfd类型)封装,通过该类设置各种事件的回调函数(例如读回调,写回调,关闭回调等)。2.可以设置自己的监听事件类型,然后根据该类型更新poller对象(epoll或者poll)对该类的操作(例如添加,修改,删除操作)。3.根据自己监听到的事件类型触发回调函数。 1.Channel.h文件 /// ///一个能被选择的 I/O channel //这个类不拥有自己的文件描述符 /// 这个文件描述符可能是socket,eventfd, timerfd,或者 signalfd. class Channel : noncopyable { public : typedef std :: function < void ( ) > EventCallback ; //事件回调 typedef std :: function < void ( Timestamp ) > ReadEventCallback ; //读事件回调 Channel ( EventLoop * loop , int fd ) ; //一个channel属于一个loop,一个loop可以对应多个channel ~ Channel ( ) ; void handleEvent ( Timestamp

evpp网络库代码分析(一)

旧巷老猫 提交于 2020-08-18 07:15:05
evpp是奇虎360内部使用的开源多线程网络库,集tcp/udp/http多种协议的服务器和客户端支持。 github 代码路径是: https://github.com/Qihoo360/evpp ,可以不依赖boost库,使用现代c++14语言(evpp/invoke_timer.cc的lambda表达式使用到了c++14的特性)进行编码。本项目高度参考了muduo网络库,而底层使用现成的libevent库作为事件驱动库,典型的一个reactor网络编程模式的例子,本文就是通过分析evpp源码来达到学习c++网络编程的效果。 muduo代码我也拜读过,muduo有个特点,它完全是为linux而写的(譬如里面用到了eventfd,timerfd以及epoll等,都是linux系统特有的,而且还跟linux版本有关,系统版本太低也不支持,譬如eventfd),而evpp做了一定的平台兼容性,能一定程度做到支持windows平台,得益于libevent库。另外,木铎有个base库,是重复造轮子了,其他还好,也是一个不可多得的多线程网络服务器编程demo,值得参考,而evpp没有像muduo那样重新实现一套基础库(如线程库、互斥锁、条件变量等),而是利用了c++14自带的std::thread、std::mutex等,相对通用很多,而且学习这些类库使用在其他项目也能用得着,毕竟是c+

evpp网络库代码分析(二)

江枫思渺然 提交于 2020-08-11 21:04:11
开局一张图! 上图是盗用自《Linux多线程服务端编程,使用muduo C++网络库》一书6.6.2章节(以及下面的时序图也是盗用该书的图)。该图列举出大部分常用的网络编程模型,当然了,这里并没有列出Boost.Asio的proactor模式。其中表中的“互通”是指多个客户端(连接)间是否能方便地交换数据,如chat聊天程序。我们的evpp库实际上是用到了“方案9”,方案9的时序图如下: 可以看出,每一个线程有一个EventLoop处理事件。这种方案是典型的“one loop per thread”流程,有一个“主EventLoop”负责accept连接,然后把连接通过round-robin(轮询调度)挂到底下的多个“子EventLoop”中(EventLoopThreadPool),每个连接都是由一个“子EventLoop”完成的,能保证请求的顺序性,也可以充分利用CPU,防止出现一个reactor的处理能力饱和,而且EventLoop线程池线程数量固定,不会因为连接数过多到达临界点(线程太多导致操作系统线程调度不过来)而性能下降! 另外还有一种网络编程模型比较常用,就是“方案8”,这个模型在muduo中有现成方案,而在evpp中需要自己实现,时序图如下: 这种方案,只有一个EventLoop,但它把计算密集的部分(decode、compute、encode

Mudo C++网络库第二章学习笔记

99封情书 提交于 2020-04-22 07:41:41
线程同步的精要 并发有两种基本的模型: 一种是message passing(消息传递); 另一种是shared memory(共享内存); 在分布式系统中(有多台物理机需要通信), 运行在多台机器上的多个进程只有一种实用模型:message passing(消息传递), 因为多个物理机基本上不能共享内存; 并发(concurrency); 线程同步的四项原则, 按重要性排列: 首要原则是尽量最低限度地共享对象, 减少需要同步的场合; 一个对象能不暴露给别的线程就不要暴露; 如果暴露就优先考虑immutable对象(const); 实在不行才暴露可修改的对象,并用同步措施来充分保护它;n 其次, 使用高级的并发编程构件, 如任务队列(TaskQueue), 生产者消费者队列(Producer-Consumer Queue), 闭锁(CountDownLatch)等; 闭锁的原理 最后不得已才使用底层同步原语(primitives)时, 只用非递归的互斥器和条件变量, 慎用读写锁, 不要用信号量; 除了使用atomic整数之外, 不要自己编写lock-free代码, 也不要用"内核级"同步原语; 不能凭空猜测'那种做法性能会更好', 比如spin lock(自旋锁) vs mutex(互斥量); 互斥量(mutex) 主要是为了保护共享数据: 用RAII(Resource

gcc/g++ 链接顺序注意事项

时光怂恿深爱的人放手 提交于 2020-04-21 20:47:13
我们知道选项-l是指定要链接的库,但是如果有多个库的时候,哪个放前面哪个放后面呢? 结论:被别的库使用的库,放在使用它的库的后面 使用下面的代码,验证一下 #include <muduo/base/Logging.h> #include <muduo/net/EventLoop.h> muduo::net::EventLoop* g_loop; int timerfd; int main(void) { muduo::net::EventLoop loop; g_loop = &loop; //LOG_INFO << "pid = " << getpid();//------① } 这段代码使用了muduo库里的base库(Logging属于base库)和net库(EventLoop属于net库)。 那么,使用下面的命令(muduo_base放在muduo_net之前了),编译它的时候,就会出错误。 $ g++ 01.cc -lmuduo_base -lmuduo_net -std=c++11 -pthread 错误信息:无法链接Logger类。 /muduo-2.0.0/muduo/net/EventLoop.cc:38: undefined reference to `muduo::Logger::Logger(muduo::Logger::SourceFile, int,

muduo网络库的学习

寵の児 提交于 2020-03-07 03:10:36
muduo是由陈硕开发的一个Linux多线程网络库,采用了很多新的Linux特性,项目代码量不到5000行,性能也不错。 是难得的一个既能用来学习,也可以在实际生产环境中使用的网络库。地址 https://github.com/chenshuo/muduo 相关介绍可以见 https://www.cnblogs.com/CodeComposer/p/4719783.html 由于此网络库只支持Linux,不支持windows,导致使用起来有点麻烦。 1.建议下载 windows移植版进行学习,地址: https://github.com/kevin-gjm/muduo-win ,用vs跑起来,加上断点真正跑一跑,就很容易明白这里面几个类的相互关系。仅仅看源码对于初学者还是有点困难。 2.先搞几个小例子跑一跑,再试试增加点feature,比如服务器监听多个端口,实现SSL等等,在这些实践中能够更好地加深对此的理解 。 3.作者本人出的书《linux多线程服务端编程》可以说是介绍这个库的很好的资料。 来源: CSDN 作者: 阿龙哥哥 链接: https://blog.csdn.net/v6543210/article/details/104698172

muduo学习笔记 - 第五章 高效的多线程日志

扶醉桌前 提交于 2020-03-03 00:54:16
第五章 高效的多线程日志 日志有两种意思: 诊断日志 交易日志 本章讲的是前一种日志,文本的供人阅读的日志,通常用于故障诊断和追踪,也可用于性能分析。 日志通常要记录: 收到的每条消息的id(关键字段,长度,hash等) 收到的每条外部消息的全文 发出每条消息的全文,每条消息都有全局唯一的id 关键部分状态的变更,等等 5.1 功能需求 日志库大体分为前端和后端两个部分 前端负责提供应用程序使用的接口API,并生成日志消息 后端负责把日志消息写到目的地 C++日志库的前端大体有两种API风格 printf的格式化输出风格 stream<<风格 stream风格的好处是当输出日志级高于语句的日志级别时,打印日志操作时个空操作,运行时开销接近零 分布式系统中的服务进程而言,日志的目的地只有一个:本地文件。往网络写日志消息时不靠谱的,因为诊断日志功能之一正是诊断网络故障,如果日志消息也是通过网络发到另一台机器就一损俱损… 本地文件作为destination,日志文件的滚动时必须的,可以简化日志的归档实现 文件大小(例如写满10GB就换下一个文件) 时间(例如每天零点新建一个日志文件,不论上一个文件是否写满) 日志文件压缩和归档,不应该是日志库应有的功能,应该交给专门的脚本去做 日志重复利用空间的功能,只会帮倒忙 往文件写日志的常见问题是,如果程序崩溃,最后几条日志信息就会丢失