muduo网络库剖析——接受新连接Acceptor类

news2024/11/17 15:50:58

muduo网络库剖析——接受新连接Acceptor类

  • 前情
    • 从muduo到my_muduo
  • 概要
  • 框架与细节
    • 成员
    • 函数
    • 使用方法
  • 源码
  • 结尾

前情

从muduo到my_muduo

作为一个宏大的、功能健全的muduo库,考虑的肯定是众多情况是否可以高效满足;而作为学习者,我们需要抽取其中的精华进行简要实现,这要求我们足够了解muduo库。

做项目 = 模仿 + 修改,不要担心自己学了也不会写怎么办,重要的是积累,学到了这些方法,如果下次在遇到通用需求的时候你能够回想起之前的解决方法就够了。送上一段话!

在这里插入图片描述

概要

对于Acceptor,是mainloop用来接收新连接的类。

框架与细节

成员

在这里插入图片描述
Acceptor在mainloop中,acceptor作为新连接的类,需要接受监听新连接。如果说poller监听比如epollpoller使用epoll_wait,pollpoller使用poll,selectpoller使用select,那acceptor监听用户端的新连接则用socket套接字。于是还需要acceptSocket套接字,另外为了acceptor本身的事件触发有所反馈,使用一个acceptChannel来监听acceptor内部发生的事件。对于发生的新连接,会使用到NewConnectionCallback新连接的回调函数来处理新连接。最后还需要acceptor的一个状态位来判断是否处在监听状态下。

函数

在这里插入图片描述
首先是Acceptor的构造函数,传参是所属的mainloop,用于监听用户端连接的网络地址listenAddr,以及是否需要复用网络端口。对于网络地址与网络端口这些套接字选项,在网络编程中是很重要的。对于acceptSocket套接字,因为在socket类中只实现了bind绑定,listen监听,accept接受用户端的新连接以及setsockopt设置套接字选项。对于套接字的创建socket还没有实现,因此我们如果要完成acceptSocket的实现,首先就是完成socket的方法,这里单独创建了一个createNonblocking。后面就是对acceptSocket的相关端口绑定和选项设定。对于Channel而言,作为一个监听新连接的类,读回调事件会经常发生,因此需要设置读回调函数,是之后需要调用到的handleRead。

在这里插入图片描述
对于createNonblocking,其实就是调用了socket函数,对于socket编程,想要更加详细了解的uu可以看我前面写的socket类的实现,里面对socket编程进行了详细的讲解。


对于Acceptor的析构函数,主要是对Channel的一些析构操作,这里我觉得可以在尝试加一些对socket的析构操作。

在这里插入图片描述
对于listen函数,其实就是将Acceptor的监听状态位置1,并让AcceptSocket进入监听状态,对Channel能对读事件感兴趣。

在这里插入图片描述
对于新连接回调函数handleRead,新连接请求肯定需要accept接受用户端的新连接,得到的连接套接字connfd如果 > 0,就调用新连接回调函数,具体逻辑就是轮询找到subLoop,唤醒,分发当前的新客户端的Channel。如果 不是 > 0,就error。对于具体的error,如果是EMFILE。

错误代码 EMFILE(文件太多)通常表示程序试图打开的文件数量超过了系统允许的最大数量。在 C++ 语言中,它是一个头文件中的宏定义,表示文件打开操作失败,并且通常会在 <sys/types.h> 或 <stdio.h> 头文件中定义。
实现原理:EMFILE 的实现可能涉及到文件打开操作的计数,当程序尝试打开一个新文件时,计数器增加。如果计数器超过某个阈值(通常是系统限制或用户定义的),则打开操作失败,返回错误代码 EMFILE。
用途:EMFILE 错误代码通常用于检测程序是否因为打开文件过多而导致了性能问题或资源不足。在现代操作系统中,文件打开操作通常是高效的,不会导致性能问题。但是,如果你的程序需要打开大量文件,可能需要考虑优化打开文件的操作,或者降低对文件的数量要求。
注意事项:在实际编程中,要正确处理 EMFILE 错误,你需要知道系统允许的最大文件打开数量(通常为1024或2048),并在程序中设置一个阈值。当尝试打开新文件时,检查当前打开的文件数量是否超过阈值,如果超过,则适当限制文件数量或采取其他优化措施。

使用方法

源码

//Acceptor.h
#pragma once

#include "noncopyable.h"
#include "Socket.h"
#include "Channel.h"

#include <functional>

class EventLoop;
class InetAddress;

class Acceptor : noncopyable {
public:
    using NewConnectionCallback = std::function<void(int sockfd, const InetAddress&)>;
    Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);
    ~Acceptor();

    void setNewConnectionCallback(const NewConnectionCallback &cb) {
        newConnectionCallback_ = cb;
    }

    bool listening() const { return listening_; }
    void listen();
private:
    void handleRead();
    
    EventLoop *loop_; // Acceptor用的就是用户定义的那个baseLoop,也称作mainLoop
    Socket acceptSocket_;
    Channel acceptChannel_;
    NewConnectionCallback newConnectionCallback_;
    bool listening_;
};
//Acceptor.cc
#include "Acceptor.h"
#include "Log.h"
#include "InetAddress.h"

#include <sys/types.h>    
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>


static int createNonblocking() {
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
    if (sockfd < 0) {
        LOG_FATAL("%s--%s--%d--%d : socket error\n", __FILE__, __FUNCTION__, __LINE__, errno);
    }
}

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
    : loop_(loop)
    , acceptSocket_(createNonblocking()) // socket
    , acceptChannel_(loop, acceptSocket_.fd())
    , listening_(false) {
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(true);
    acceptSocket_.bind(listenAddr); // bind
    // TcpServer::start() Acceptor.listen  有新用户的连接,要执行一个回调(connfd=》channel=》subloop)
    // baseLoop => acceptChannel_(listenfd) => 
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}

Acceptor::~Acceptor() {
    acceptChannel_.disableAll();
    acceptChannel_.remove();
}

void Acceptor::listen() {
    listening_ = true;
    acceptSocket_.listen(); // listen
    acceptChannel_.enableReading(); // acceptChannel_ => Poller
}

// listenfd有事件发生了,就是有新用户连接了
void Acceptor::handleRead() {
    InetAddress peerAddr;
    int connfd = acceptSocket_.accept(peerAddr);
    if (connfd >= 0)
    {
        if (newConnectionCallback_)
        {
            newConnectionCallback_(connfd, peerAddr); // 轮询找到subLoop,唤醒,分发当前的新客户端的Channel
        }
        else
        {
            ::close(connfd);
        }
    }
    else
    {
        LOG_ERROR("%s--%s--%d--%d : accept error\n", __FILE__, __FUNCTION__, __LINE__, errno);
        if (errno == EMFILE)
        {
            LOG_ERROR("%s--%s--%d--%d : sockfd reached limit error\n", __FILE__, __FUNCTION__, __LINE__, errno);
        }
    }
}

结尾

以上就是接受新连接Acceptor类的相关介绍,以及我在进行项目重写的时候遇到的一些问题,和我自己的一些心得体会。发现写博客真的会记录好多你的成长,而且对于一个好的项目,写博客也是证明你确实有过深度思考,并且在之后面试或者工作时遇到同样的问题能够进行复盘的一种有效的手段。所以,希望uu们也可以像我一样,养成写博客的习惯,逐渐脱离菜鸡队列,向大佬前进!!!加油!!!

也希望我能够完成muduo网络库项目的深度学习与重写,并在功能上能够拓展。也希望在完成这个博客系列之后,能够引导想要学习muduo网络库源码的人,更好地探索这篇美丽繁华的土壤。致敬chenshuo大神!!!

鉴于博主只是一名平平无奇的大三学生,没什么项目经验,所以可能很多东西有所疏漏,如果有大神发现了,还劳烦您在评论区留言,我会努力尝试解决问题!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418046.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hbuilder真机调试

1.找到adbs文件&#xff0c;执行adb.exe文件 2.手机找到开发人员选项&#xff08;设置-系统和更新-开发人员选项&#xff09; 打开之后在调试里面打开USB调试&#xff0c;数据线连接电脑 手机会弹窗提示&#xff0c;点击确定 然后就准备运行啦 3.Hbuilder运行 点击运行就可以…

Kotlin快速入门5

Kotlin的继承与重写 kotlin的继承 Kotlin中所有类都继承自Any类&#xff0c;Any类是所有类的超类&#xff0c;对于没有超类型声明的类是默认超类&#xff08;Any 不是 java.lang.Object&#xff09;&#xff1a; class LearnKotlin // 默认继承自Any Any类默认提供三个函数…

LeetCode力扣题解(随机每日一题)——使数组为空的最少操作次数

目录 题目链接 题目描述 输入输出示例 代码 复杂度分析 题目链接 2870. 使数组为空的最少操作次数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个下标从 0 开始的正整数数组 nums 。 你可以对数组执行以下两种操作 任意次 &#xff1a; 从数组中选择 两个…

Ubuntu的应用

记得看目录哦&#xff01; 1. root用户1.1 root介绍1.2 hello Python 2. APT2.1 APT原理机制2.2 Ubuntu软件操作的相关命令2.3 更新Ubuntu软件下载地址2.4 安装一下vim2.5 使用vim 3. 远程登录Ubuntu3.1 ssh介绍3.2 原理图3.3 安装ssh3.4 安装net-tools3.5 查看端口号&#xff…

【Web前端实操17】导航栏效果——滑动门

滑动门 定义: 类似于这种: 滑到导航栏的某一项就会出现相应的画面,里面有对应的画面出现。 箭头图标操作和引用: 像一些图标,如果需要的话,可以找字体图标,比如阿里巴巴矢量图标库:iconfont-阿里巴巴矢量图标库 选择一个——>添加至购物车——>下载代码 因…

Abp 从空白WebApplication开始

开发环境&#xff1a;VS2022、.NET6 1、创建项目&#xff1a;BasicAspNetCoreApplication 2、NuGet添加&#xff1a;Volo.Abp.AspNetCore.Mvc和Volo.Abp.Autofac&#xff0c;如下图所示&#xff1a; 3、开始写代码&#xff0c;目录如下图所示&#xff1a; 3.1、添加启动模块Ap…

2023年:个人年度成长与团队协作成就

文章目录 个人职业发展的喜悦团队成就的辉煌公众号CSDN申请了移动安全领域新星创作者获得6月城市之星北京TOP 10获得23年博客之星TOP 41年度总结 知识星球 开拓新领域的决心免费知识大陆付费知识大陆 展望未来福利时间知识星球会员一年知识星球立减88券 在这个充满挑战与机遇的…

【Spark系列1】DAG中Stage和Task的划分全流程

本文字数在7800字左右&#xff0c;预计时间在15分钟 一、整体流程 每个Aciton操作会创建一个JOB&#xff0c;JOB会提交给DAGScheduler&#xff0c;DAGScheduler根据RDD依赖的关系划分为多个Stage&#xff0c;每个Stage又会创建多个TaskSet&#xff0c;每个TaskSet包含多个Tas…

解决 微信退款,本地退款可以,但是测试环境退款失败问题

1.问题描述 微信小程序退款操作&#xff0c;测试环境一直退款失败&#xff0c;但是本地测试退款却没问题 2.原因分析 本地退款可以&#xff0c;但是测试环境不行&#xff0c;说明问题出在测试环境上 经过排查发现是测试环境的微信商户证书是另一个小程序的&#xff0c;不是正在…

【Delphi】系统菜单中增加菜单项

目录 一、问题提出 二、程序截图 ​编辑 ​编辑 三、程序代码&#xff1a; 一、问题提出 我们在开发windows程序的时候&#xff0c;可能会希望在窗体的系统菜单中增加一个菜单项&#xff0c;那么如何实现呢&#xff0c;实际上通过调用windows API是可以实现的&#xff0c;…

C++初阶:C/C++内存管理、new与delete详解

之前结束了类与对象&#xff1a;今天进行下面部分内容的学习 文章目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.C动态内存管理方式3.1new/delete操作内置类型3.2new和delete操作自定义类型 4.operator new与operator delete函数5.new和…

安科瑞AAFD系列故障电弧探测器应用以及选型

功能&#xff1a; 故障电弧探测器&#xff08;以下简称探测器&#xff09;对接入线路中的故障电弧&#xff08;包括故障并联电弧、故障串联电弧&#xff09;进行有效的检测&#xff0c;当检测到线路中存在引起火灾的故障电弧时&#xff0c;可以进行现场的声光报警&#xff0c;…

MYSQL中group by分组查询的用法详解(where和having的区别)!

文章目录 前言一、数据准备二、使用实例1.如何显示每个部门的平均工资和最高工资2.显示每个部门的每种岗位的平均工资和最低工资3.显示平均工资低于2000的部门和它的平均工资4.having 和 where 的区别5.SQL查询中各个关键字的执行先后顺序 前言 在前面的文章中&#xff0c;我们…

MySQL知识点总结(一)——一条SQL的执行过程、索引底层数据结构、一级索引和二级索引、索引失效、索引覆盖、索引下推

MySQL知识点总结&#xff08;一&#xff09;——一条SQL的执行过程、索引底层数据结构、一级索引和二级索引、索引失效、索引覆盖、索引下推 一条SQL的执行过程索引底层数据结构为什么不使用二叉树&#xff1f;为什么不使用红黑树?为什么不使用hash表&#xff1f;为什么不使用…

elementUI的el-select传递item对象或其他参数的2种方法

方法1 :value“item” 绑定对象 只要:value绑定item对象就可以 value-key"value" 必须是item里的一个属性&#xff0c;绑定值为对象类型时必填 <el-select v-model"value" placeholder"请选择" value-key"value" change"cha…

nginx部署前端(vue)项目及配置修改

目录 一、前端应用打包 二、部署前端应用 1、上传前端文件夹 2、修改nginx配置文件 3、重启nginx 三、查看效果 nginx安装参考&#xff1a;linux安装nginx-CSDN博客 一、前端应用打包 打包命令 npm run build 打包成功如下&#xff0c;会在项目路径下生成dist文件夹 二…

为什么说2023年是AI元年

前言 思考者~ 2023年被称为AI元年&#xff0c;主要是因为在这一年中&#xff0c;人工智能技术在各个领域取得了突破性的进展和应用&#xff0c;这些技术的广泛应用深刻地影响了人们的生活和工作方式&#xff0c;也预示着未来AI技术的更大潜力和发展空间。 首先&#xff0c;…

【数据分析】numpy基础第二天

文章目录 前言数组的形状变换reshape的基本介绍使用reshapereshape([10, 1])运行结果reshape自动判断形状reshape([-1, 1])运行结果 合并数组使用vstack和hstackvstack和hstack的运行结果使用concatenateconcatenate运行结果 分割数组array_split运行结果 数组的条件筛选条件筛…

C++ 数论相关题目 求组合数I

直接按照公式求解会超时。 常用组合数递推式。 因此用递推式先预处理出来&#xff0c;然后查表。 #include <iostream> #include <algorithm>using namespace std;const int N 2010, mod 1e9 7;int c[N][N];void init() {for(int i 0; i < N; i )for(in…