当前位置: 首页 > news >正文

手写muduo网络库(七):深入剖析 Acceptor 类

引言

在网络编程中,服务器端程序需要能够监听客户端的连接请求并进行处理。Acceptor类在这个过程中扮演着至关重要的角色,它负责创建监听套接字、绑定地址、开始监听以及处理新的连接请求。在本文中,我们将详细剖析手写 muduo 网络库中的Acceptor类,探讨其实现原理和工作流程。

整体功能概述

Acceptor类的主要功能是在指定的地址和端口上监听客户端的连接请求,并在有新的连接到来时调用相应的回调函数进行处理。它封装了套接字的创建、绑定、监听以及接受连接等操作,使得上层代码可以更方便地处理网络连接。

代码结构分析

头文件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 listenning() const { return listenning_; }void listen();private:void handleRead();EventLoop *loop_;Socket acceptSocket_;Channel acceptChannel_;NewConnectionCallback NewConnectionCallback_;bool listenning_;
};

代码解释

  1. 类型定义
    • NewConnectionCallback:这是一个函数对象类型,用于处理新的连接。当有新的客户端连接到来时,会调用这个回调函数,并传递新连接的套接字描述符和客户端地址。
  2. 构造函数
    • Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport):接受一个EventLoop指针、一个InetAddress对象和一个布尔值reuseport作为参数。EventLoop用于事件循环,InetAddress表示监听的地址和端口,reuseport用于设置是否重用端口。
  3. 析构函数
    • ~Acceptor():负责清理资源,如禁用Channel的所有事件并移除Channel
  4. 成员函数
    • setNewConnectionCallback:用于设置新连接的回调函数。
    • listenning:返回当前是否正在监听。
    • listen:开始监听客户端的连接请求。
  5. 私有成员函数
    • handleRead:处理读事件,当有新的连接到来时会触发该函数。
  6. 私有成员变量
    • loop_:指向EventLoop的指针。
    • acceptSocket_:用于监听的套接字对象。
    • acceptChannel_:用于处理套接字事件的Channel对象。
    • NewConnectionCallback_:新连接的回调函数。
    • listenning_:表示当前是否正在监听的布尔值。

源文件Acceptor.cpp

#include "Acceptor.h"
#include "LogStream.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, IPPROTO_TCP);if (sockfd < 0){LOG_ERROR << "listen socket create err: " << errno;exit(-1);}return sockfd;
}Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport): loop_(loop), acceptSocket_(createNonblocking()), acceptChannel_(loop, acceptSocket_.fd()), listenning_(false)
{acceptSocket_.setReuseAddr(true);acceptSocket_.setReusePort(true);acceptSocket_.bindAddress(listenAddr);acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));
}Acceptor::~Acceptor()
{acceptChannel_.disableAll();acceptChannel_.remove();
}void Acceptor::listen()
{listenning_ = true;acceptSocket_.listen();acceptChannel_.enableReading();
}void Acceptor::handleRead()
{InetAddress peerAddr;int connfd = acceptSocket_.accept(&peerAddr);if (connfd >= 0){if (NewConnectionCallback_){NewConnectionCallback_(connfd, peerAddr);}else{::close(connfd);}}else{LOG_ERROR << "accept err: " << errno;if (errno == EMFILE){LOG_ERROR << "sockfd reached limit ";}}
}
代码解释
  1. 辅助函数createNonblocking
    • 该函数用于创建一个非阻塞的套接字。使用::socket函数创建一个AF_INET类型的流式套接字,并设置为非阻塞和CLOEXEC模式。如果创建失败,会输出错误日志并退出程序。
  2. 构造函数Acceptor::Acceptor
    • 调用createNonblocking函数创建一个非阻塞的套接字。
    • 设置套接字的地址重用和端口重用选项。
    • 将套接字绑定到指定的地址和端口。
    • acceptChannel_设置读事件的回调函数,当有新的连接到来时会调用handleRead函数。
  3. 析构函数Acceptor::~Acceptor
    • 禁用acceptChannel_的所有事件,并从EventLoop中移除acceptChannel_
  4. 成员函数Acceptor::listen
    • listenning_标志设置为true,表示开始监听。
    • 调用acceptSocket_.listen()开始监听客户端的连接请求。
    • 启用acceptChannel_的读事件,以便在有新的连接到来时触发handleRead函数。
  5. 成员函数Acceptor::handleRead
    • 调用acceptSocket_.accept函数接受新的连接,并获取客户端的地址。
    • 如果接受连接成功,且设置了新连接的回调函数,则调用该回调函数处理新连接;否则,关闭新连接的套接字。
    • 如果接受连接失败,输出错误日志,并在errnoEMFILE时提示套接字描述符达到上限。

工作流程总结

  1. 创建Acceptor对象:在服务器启动时,创建一个Acceptor对象,并传入EventLoop指针、监听地址和端口信息。
  2. 设置回调函数:调用setNewConnectionCallback函数设置新连接的回调函数。
  3. 开始监听:调用listen函数开始监听客户端的连接请求。
  4. 处理新连接:当有新的连接到来时,acceptChannel_会触发读事件,调用handleRead函数。在handleRead函数中,接受新的连接,并调用预先设置的回调函数处理新连接。

总结

Acceptor类通过封装套接字的创建、绑定、监听和接受连接等操作,为上层代码提供了一个简单易用的接口来处理客户端的连接请求。通过使用EventLoopChannel,可以实现高效的事件驱动的网络编程。在实际应用中,我们可以根据需要修改NewConnectionCallback函数,以实现不同的业务逻辑。

希望本文对你理解Acceptor类的实现原理和工作流程有所帮助。在后续的文章中,我们将继续深入探讨手写 muduo 网络库的其他部分。


文章转载自:

http://L2h2Ie12.xjmzy.cn
http://QIjPiYZY.xjmzy.cn
http://nVDPxKba.xjmzy.cn
http://OHbzKMDx.xjmzy.cn
http://87sHQHgF.xjmzy.cn
http://mYoez52a.xjmzy.cn
http://ifbGlDWH.xjmzy.cn
http://x3rEUvbC.xjmzy.cn
http://LgKFiONi.xjmzy.cn
http://ET0iZ2Cx.xjmzy.cn
http://qizwKG5i.xjmzy.cn
http://J35BAGJS.xjmzy.cn
http://vIxOCVv8.xjmzy.cn
http://IxLTavtk.xjmzy.cn
http://xWBtJBKy.xjmzy.cn
http://c6AZsBP9.xjmzy.cn
http://6HdeNK6H.xjmzy.cn
http://DDVHv5Dv.xjmzy.cn
http://CBvGY7BR.xjmzy.cn
http://zSc9mx0B.xjmzy.cn
http://lAaGORIG.xjmzy.cn
http://J4G4LwiP.xjmzy.cn
http://8wtzWvLi.xjmzy.cn
http://wEB64Ayz.xjmzy.cn
http://nK4V8F7X.xjmzy.cn
http://0r4exdD8.xjmzy.cn
http://zt4eDhWq.xjmzy.cn
http://ktnhhCZK.xjmzy.cn
http://NC9GFGIT.xjmzy.cn
http://35lHDwKx.xjmzy.cn
http://www.dtcms.com/a/245853.html

相关文章:

  • 如何正确评估服务器CPU/内存/IO利用率 (性能过剩or瓶颈)
  • SpringBoot后端开发知识点总结(持续更新)
  • Nginx(自用)
  • 会技术的产品经理
  • mt6739 Android12出现 red state
  • uniapp开发的app和原生的app开发各有什么优缺点
  • 数据结构 - Java 队列
  • MybatisPlus-DQL查询+DML
  • Rust 学习笔记:处理任意数量的 future
  • Odoo 18 库存中管理最低安全库存规则(再订货规则)
  • 【WebSocket】WebSocket架构重构:从分散管理到统一连接的实战经验
  • 【EdgeAI实战】(3)边缘AI开发套件 STM32N6570X0 用户手册
  • Jenkins 配置gitlab的 pipeline流水线任务
  • reactive() 和 toRef()
  • VR 地震安全演练:“透视” 地震,筑牢企业安全新护盾​
  • 单连杆倾角估计:互补滤波器的 MATLAB 仿真实现
  • jenkins流水线打包vue无权限
  • VR百科:实景三维重建
  • Linux系统下安装elasticsearch6.8并配置ik分词
  • Vue3+PDF.js 实现高性能 PDF 阅读器开发实战
  • VSCode占C盘内存太大,如何处理
  • 2025最新版使用VSCode和CMake图形化编译调试Cuda C++程序(保姆级教学)
  • vba学习系列(11)--批退率通过率等数据分析
  • 企业级人员评价系统Web端重构实战:前端架构效能升级
  • OpenCV 图像几何形状绘制
  • LMD分解通过局部均值分解重构信号实现对信号的降噪
  • MySQL数据库:关系型数据库的基石
  • 知识变现新范式:创客匠人如何重构中医行业商业逻辑
  • GESP 七级 实操题
  • 以太联16端口千兆PoE+网络管理交换机