Muduo库源码剖析(十)——总结

2023-11-17

Muduo网络库的核心代码模块

Channel

封装fd的对应事件变化情况,和关注事件

fd 、events、 revents、 callbacks,

两种channel: listenfd-acceptorChannel, connfd-connectionChannel

Poller 和 EPollPoller - Demultiplex

std::unordered_map<int, Channel*> channels_

EventLoop - Reactor

负责 Channel 和Poller通信

std::vector<Channel*> activeChannels_

std::unique_ptr<Poller> poller_

int wakeupFd_; // 当mainloop获取一个新用户的channel,通过轮询算法选择一个subloop,通过该fd唤醒对应subloop来执行Channel回调

std::unique_ptr<Channel> wakeupChannel_;

Thread 和 EventLoopThread

c++11线程封装 以及 one loop per thread 的封装

EventLoopThreadPool

getNextLoop()RR方式获取下一个 subLoop

Socket

Acceptor

特殊的TcpConnection? 封装listenfd相关操作如bind listen, 在baseLoop中进行检测。

Buffer

参考Netty的实现, prendable, readerindex, writerindex

TcpConnection

封装 成功建立连接的connfd,一个connfd唯一对应一个TcpConnection ,关键成员有:Socket对象、Channel对象、发送和接受缓冲区;

TcpConnectionChannel各种事件回调的设置

TcpServer

关键成员有:AcceptorEventLoopThreadPoolstd::unordered_map<std::string, TcpConnectionPtr>connections_

重点总结

应用技术

C++11、 Socket、 Reactor模型、多线程、epoll

项目描述

在对源码剖析的同时也针对Muduo网络库进行基于C++11的重新实现,保留关键部分,使其不用依赖boost库就可以实现主要功能。

此项目是一个基于Muduo库和C++11的高性能网络库,能够让用户实现稳定、可靠、高性能、高并发的服务器应用程序

主要工作

  • 采用 multi-Reactorsone loop per thread + nonblocking IO的网络模型实现IO处理和业务处理的分离;
  • 对C++11线程类进行封装实现 one loop per thread的一对一绑定关系;
  • 使用C++11特性取代boost库在muduo中的使用,如shared_ptr、bind、atomic等
  • 使用eventfd()来实现 mainLoopsubLoop的线程间通知操作,相比于使用任务队列需要加锁降低了开销

项目核心逻辑介绍

我自己对Muduo库网络部分核心做了一个总结

在这里插入图片描述

Muduo库采用one loop per thread + nonblocking IO网络模型,在mainLoop中 关注listenfd的读事件,并且将该listenfd封装成一个特殊的 TcpConnection 类即 Acceptor ,因为其读事件的处理是调用 accept 建立连接,并且将连接发送到一个subLoop上,选择subLoop的方法采用轮询的方法。

EventLoop模块

首先是最重要的Reactor模块,在Muduo库中也就是EventLoop 类的,其中主要的功能负责调度和分发网络事件即调用 epoll_wait 监视fd 感兴趣的事件,并执行对应的回调函数,在Muduo中 将这一个逻辑流程进行面向对象的拆解,将epoll_wait 封装到 EPollPoller中,将fd 以及其感兴趣的事件和对应回调封装到Channel中。EventLoop 负责 Channel 和 Poller的通信EventLoop::loop() 调用 Poller进行检测事件,当有事件到来,再通过EventLoop调用 Channel中由TcpConnection和 用户 设置的相应事件回调。

eventfd()

注意到这里每一个Loop 与 其构造时的线程一一对应,在subLoop初始化时,会自动创建一个 eventfd()来供mainLoop唤醒它本身,这样就不需要使用生产者消费者队列来管理任务,从而避免subLoop争抢任务而要将队列加锁带来的开销。

缓冲区Buffer

非阻塞网络编程中应用层buffer是必须的:非阻塞IO的核心思想是避免阻塞在read()或write()或其他I/O系统调用上,这样可以最大限度复用thread-of-control,让一个线程能服务于多个socket连接。I/O线程只能阻塞在IO-multiplexing函数上,如select()/poll()/epoll_wait()。这样一来,应用层的缓冲是必须的,每个TCP socket都要有inputBufferoutputBufferTcpConnection必须有output buffer:使程序在write()操作上不会产生阻塞,当write()操作后,操作系统一次性没有发送完时,网络库把剩余数据则放入outputBuffer中,然后注册POLLOUT事件,一旦socket变得可写,则立刻调用write()进行写入数据。即将应用层buffer数据拷贝到操作系统buffer。
TcpConnection必须有input buffer:当发送方send数据后,接收方收到数据不一定是整个的数据,网络库在处理socket可读事件的时候,必须一次性把socket里的数据读完(加一个栈空间的数组用readv分散读),否则会反复触发POLLIN事件,造成busy-loop。所以muduo库为了应对数据不完整的情况,收到的数据先放到inputBuffer里。——操作系统buffer到应用层buffer。

muduo库 connfd 采用触发模式是LT,这样优点是

  • 不会丢失数据或者消息
    应用没有读取完数据,内核是会不断上报的

  • 低延迟处理
    每次读数据只需要一次系统调用;照顾了多个连接的公平性,不会因为某个连接上的数据量过大而影响其他连接处理消息

  • 跨平台处理

    像select一样可以跨平台使用

Muduo几大网络组件的抽象如下

在这里插入图片描述

项目多处使用C++11特性如原子变量实现无锁编程,以及智能指针对资源进行安全管理。

个人收获

能更加熟练地运用C++11新特性,如bind、智能指针和原子变量等语法,同时我对于one loop per thread + nonblocking IO网络模型的理解得到了提升。此外,在编码中对类的封装和设计的实践,让我对设计模式、基于事件驱动的编程方法以及事件回调有了更具体的认识。

遇到问题

原子变量未初始化

服务器没有启动监听Acceptor::listen,排查后发现

TcpServer::start() 中 执行该函数的判断变量没有初始化,导致判断条件不成立,无法执行Acceptor::listen

即控制TcpServer::start()只执行一次的**原子变量started_**未初始化,数值为65538

accept问题

调用aceept处出错,错误码为 errno = 22

通过 perror查询发现是invalid argument,查阅资料后发现具体是因为

  1. accept函数参数不合法

  2. 对返回的connfd没设置非阻塞,因为本项目是Reactor模型 one loop per thread + non-blocking IO

为了方便起见,不想再多写fcntl,于是采用accept4替代 accept

sockaddr_in addr;
socklen_t len = sizeof addr;
bzero(&addr, sizeof addr);
// sockfd是listen用
int connfd = ::accept4(sockfd_, (sockaddr*)&addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK);

杂项

ps -ef | grep testserver 查找程序对应的进程号

在这里插入图片描述

netstat -anpt 查看网络状态,如下表明./testserver在本机8888端口处于监听状态

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Muduo库源码剖析(十)——总结 的相关文章

  • fopen 不返回

    我在 C 程序中使用 fopen 以只读模式 r 打开文件 但就我而言 我观察到 fopen 调用没有返回 它不返回 NULL 或有效指针 执行在 fopen 调用时被阻止 文件补丁绝对正确 我已经验证过 并且不存在与权限相关的问题 任何人
  • 加载数据infile,Windows和Linux的区别

    我有一个需要导入到 MySQL 表的文件 这是我的命令 LOAD DATA LOCAL INFILE C test csv INTO TABLE logs fields terminated by LINES terminated BY n
  • 所有平台上的java

    如果您想用 java 为 Windows Mac 和 Linux 编写桌面应用程序 那么所有这些代码都相同吗 您只需更改 GUI 即可使 Windows 应用程序更像 Windows 等等 如果不深入细节 它是如何工作的 Java 的卖点之
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 安装J语言的JQt IDE,出现错误

    我一直按照这里的说明进行操作 http code jsoftware com wiki System Installation Linux http code jsoftware com wiki System Installation L
  • ubuntu:升级软件(cmake)-版本消歧(本地编译)[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我的机器上安装了 cmake 2 8 0 来自 ubuntu 软件包 二进制文件放置在 usr bin cmake 中 我需要将 cmake 版本至少
  • 如何通过保持目录结构完整来同步路径中匹配模式的文件?

    我想将所有文件从服务器 A 复制到服务器 B 这些文件在不同级别的文件系统层次结构中具有相同的父目录名称 例如 var lib data sub1 sub2 commonname filetobecopied foo var lib dat
  • 在我的 index.php 中加载 CSS 和 JS 等资源时出现错误 403

    我使用的是 Linux Elementary OS 并在 opt 中安装了 lampp My CSS and JS won t load When I inspect my page through browser The console
  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 我的线程图像生成应用程序如何将其数据传输到 GUI?

    Mandelbrot 生成器的缓慢多精度实现 线程化 使用 POSIX 线程 Gtk 图形用户界面 我有点失落了 这是我第一次尝试编写线程程序 我实际上并没有尝试转换它的单线程版本 只是尝试实现基本框架 到目前为止它是如何工作的简要描述 M
  • ftrace:仅打印trace_printk()的输出

    是否可以只转储trace printk 输出于trace文件 我的意思是过滤掉函数跟踪器 或任何其他跟踪器 中的所有函数 一般来说 您可以在选项目录中关闭选项 sys kernel debug tracing options Use ls显
  • 如何确保应用程序在 Linux 上持续运行

    我试图确保脚本在开发服务器上保持运行 它会整理统计数据并提供网络服务 因此它应该会持续存在 但一天中有几次 它会因未知原因而消失 当我们注意到时 我们只需再次启动它 但这很麻烦 并且某些用户没有权限 或专有技术 来启动它 作为一名程序员 我
  • CoAP数据包的大小是多少?

    我是这项技术的新手 有人可以帮助我了解一些疑问吗 Q 1 CoAP数据包的大小是多少 我知道有 4 字节固定标头 但是包括标头 选项和负载在内的最大大小限制是多少 Q 2 有像MQTT那样的Keep Alive的概念吗 它在UDP上工作 它
  • 当 grep "\\" XXFile 我得到“尾随反斜杠”

    现在我想查找是否有包含 字符的行 我试过grep XXFile但它暗示 尾随反斜杠 但当我尝试时grep XXFile没关系 谁能解释一下为什么第一个案例无法运行 谢谢 区别在于 shell 处理反斜杠的方式 当你写的时候 在双引号中 sh
  • 按进程名称过滤并记录 CPU 使用情况

    Linux 下有选项吗顶部命令 https www man7 org linux man pages man1 top 1 html我可以在哪里按名称过滤进程并将每秒该进程的 CPU 使用情况写入日志文件 top pgrep 过滤输出top
  • 如何减去两个 gettimeofday 实例?

    我想减去两个 gettimeofday 实例 并以毫秒为单位给出答案 这个想法是 static struct timeval tv gettimeofday tv NULL static struct timeval tv2 gettime
  • 监视目录的更改

    很像一个类似的问题 https stackoverflow com questions 112276 directory modification monitoring 我正在尝试监视 Linux 机器上的目录以添加新文件 并希望在这些新文
  • 确定我可以向文件句柄写入多少内容;将数据从一个 FH 复制到另一个 FH

    如何确定是否可以将给定数量的字节写入文件句柄 实际上是套接字 或者 如何 取消读取 我从其他文件句柄读取的数据 我想要类似的东西 n how much can I write w handle n read r handle buf n a
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐