C++仿muduo库高并发服务器项目:TcpServer模块

前言

本篇文章所讲的是本人的个人项目仿muduo库高并发服务器中的TcpServer模块实现部分。

TcpServer模块介绍:

功能概述
  1. 设置从属线程池数量。
  2. 启动服务器。
  3. 设置多种回调函数(连接建立完成、消息、关闭、任意等),用户设置给 TcpServerTcpServer 再设置给获取的新连接。
  4. 选择是否启动非活跃连接超时销毁功能。
  5. 具备添加定时任务功能。
流程
  1. TcpServer 中实例化 Acceptor 对象和 EventLoop 对象(baseloop)。
  2. Acceptor 挂到 baseloop 上进行事件监控。
  3. Acceptor 对象就绪可读事件时,执行读事件回调函数获取新建连接。
  4. 对新连接,创建 Connection 进行管理。
  5. 对连接对应的 Connection 设置功能回调(连接完成回调、消息回调、关闭回调、任意事件回调)。
  6. 启动 Connection 的非活跃连接的超时销毁规则。
  7. 将新连接对应的 Connection 挂到 LoopThreadPool 中的从属线程对应的 EventLoop 中进行事件监控。
  8. 一旦 Connection 对应的连接就绪了可读事件,则执行读事件回调函数,读取数据,读取完毕后调用 TcpServer 设置的消息回调。
成员介绍

成员变量

uint64_t _next_id

  • 作用 :这是一个全局自增的计数器。
  • 用途 :每当有一个新连接到来时, _next_id 会加 1,作为这个新连接的唯一 ID ( conn_id )。这用于在 _conns 哈希表中唯一标识和查找连接。

int _port

  • 作用 :服务器监听的端口号。

int _timeout

  • 作用 :非活跃连接的超时时间(单位:秒)。
  • 用途 :如果启用了非活跃销毁功能,当连接在 _timeout 秒内没有任何读写事件发生时,服务器会自动断开该连接。

bool _enable_inactive_release

  • 作用 :功能开关。
  • 用途 :标记是否开启了"非活跃连接自动销毁"功能。

EventLoop _baseloop

  • 作用 : 主 Reactor(Main Loop) 。
  • 用途 :这是运行在主线程中的事件循环。它 只负责 一件事情:监听监听套接字(ListenFd)的事件,也就是处理新用户的连接请求。一旦接受连接,它就会把新连接分发给线程池中的从 Reactor。

Acceptor _acceptor

  • 作用 :连接接受器。
  • 用途 :它内部封装了监听 Socket。它运行在 _baseloop 中,当有新连接到来时,它会执行 accept 系统调用拿到文件描述符,然后调用 TcpServer::NewConnection 回调函数。

LoopThreadPool _pool

  • 作用 : 从 Reactor 线程池(Sub Loops) 。
  • 用途 :管理一组 IO 线程,每个线程运行一个 EventLoop 。当 _baseloop 接收到新连接后,会通过 _pool 选择一个 IO 线程,将新连接交给它管理。

std::unordered_map<uint64_t, PtrConnection> _conns

  • 作用 :连接容器。
  • 用途 :保存服务器当前所有活跃连接的智能指针 ( shared_ptr )。
  • 关键点 :这是服务器持有连接生命周期的地方。只要连接在这个 map 中,它的引用计数就不为 0,就不会被析构。当连接关闭时,从这里移除,引用计数归零,连接自动销毁。

回调函数对象 ( _connected_callback , _message_callback , _closed_callback , _event_callback )

  • 作用 :保存用户注册的业务逻辑。
  • 用途 :当新连接创建时,这些回调会被传递给 Connection 对象。

成员函数

void NewConnection(int fd)

  • 触发时机 :当 Acceptor 检测到新连接请求并成功执行 accept 后调用。
  • 逻辑流程 :
    1. _next_id++ :生成新连接 ID。
    2. _pool.NextLoop() :从线程池中轮询选出一个 EventLoop (负载均衡)。
    3. new Connection(...) :创建新连接对象,绑定到选定的 Loop 和 fd 上。
    4. 配置连接 :将用户设置的各种回调函数(OnMessage, OnClose 等)传递给这个新连接。
    5. 设置内部关闭回调 :这是关键一步,当连接断开时,Connection 对象会回调这个函数,通知 TcpServer 从 _conns 中移除自己。
    6. 启动超时销毁 :如果 _enable_inactive_release 为真,开启连接的定时器。
    7. 初始化 :调用 conn->Established() ,正式开启读事件监控。
    8. 存储 : _conns.insert(...) ,将连接放入 map 中管理。

void RemoveConnection(const PtrConnection& conn)

  • 触发时机 :当 Connection 检测到连接断开(对端关闭或错误)并执行清理流程时调用。
  • 作用 :它只是一个跳板,为了线程安全,它调用 _baseloop.RunInLoop 将实际的删除操作调度到主线程执行。

void RemoveConnectionInLoop(const PtrConnection& conn)

  • 执行环境 :主线程 ( _baseloop )。
  • 逻辑 :根据连接 ID 从 _conns map 中删除对应的 Connection 对象。
  • 结果 : shared_ptr 引用计数减 1。如果此时没有其他地方引用该连接(通常情况),连接对象就会被析构,从而释放 Socket 资源。

void RunAfterInLoop(const Functor task, int delay)

  • 作用 :在主循环中添加定时任务的辅助函数。

TcpServer(int port)

  • 构造函数 :
    1. 初始化端口。
    2. 初始化 _acceptor ,传入 _baseloop 和端口。
    3. 初始化 _pool ,传入 _baseloop 。
    4. 关键绑定 : _acceptor.SetAcceptCallback(...) ,将 TcpServer::NewConnection 绑定为接受连接的回调。
    5. _acceptor.Listen() :立即开始监听端口(此时还没有开始事件循环)

void SetThreadCount(int count)

  • 作用 :设置 IO 线程的数量。
  • 说明 :
    • count = 0 :单线程模式。所有连接的 IO 操作都在主线程 ( _baseloop ) 中处理。
    • count > 0 :多线程模式。主线程只负责 Accept, count 个子线程负责处理连接 IO。

void Start()

  • 作用 :启动服务器。
  • 逻辑 :
    1. _pool.Create() :创建并启动所有 IO 线程。
    2. _baseloop.Start() :主线程开始进入 epoll_wait 事件循环,正式开始工作。这是一个死循环,函数调用后不会返回。

源代码:

c++ 复制代码
class TcpServer
{
private:
    uint64_t _next_id;                                      //自动增长的连接ID
    int _port;
    int _timeout;                                           //超时时间
    bool _enable_inactive_release;                          //是否启用非活跃销毁
    EventLoop _baseloop;                                    //主线程的EventLoop对象,负责监听事件的处理
    Acceptor _acceptor;                                     //监听套接字管理对象
    LoopThreadPool _pool;                                   //从属EventLoop线程池
    std::unordered_map<uint64_t,PtrConnection> _conns;      //保存管理所有连接对应的shared_ptr对象

private:
    using ConnectedCallback = std::function<void(const PtrConnection&)>;
    using MessageCallback = std::function<void(const PtrConnection&, Buffer *)>;
    using ClosedCallback = std::function<void(const PtrConnection&)>;
    using AnyEventCallback = std::function<void(const PtrConnection&)>;
    using Functor = std::function<void()>;
    ConnectedCallback _connected_callback;
    MessageCallback _message_callback;
    ClosedCallback _closed_callback;
    AnyEventCallback _event_callback;

    //为新连接构造一个Connnection进行管理
    void NewConnection(int fd)
    {
        _next_id++;
        PtrConnection conn(new Connection(_pool.NextLoop(), _next_id, fd));
        conn->SetMessageCallback(_message_callback);
        conn->SetClosedCallback(_closed_callback);
        conn->SetConnectedCallback(_connected_callback);
        conn->SetAnyEventCallback(_event_callback);
        conn->SetSrvClosedCallback(std::bind(&TcpServer::RemoveConnection,this,conn));
        if(_enable_inactive_release) conn->EnableInactiveRelease(_timeout);//启动非活跃超时销毁
        conn->Established();//就绪初始化
        _conns.insert(std::make_pair(_next_id, conn));
    }
    void RemoveConnectionInLoop(const PtrConnection& conn)
    {
        uint64_t id = conn->Id();
        auto it = _conns.find(id);
        if(it != _conns.end())
        {
            _conns.erase(it);
        }
    }
    //从_conns中移除连接信息
    void RemoveConnection(const PtrConnection& conn)
    {
        _baseloop.RunInLoop(std::bind(&TcpServer::RemoveConnectionInLoop,this,conn));
    }
    void RunAfterInLoop(const Functor task,int delay)
    {
        _next_id++;
        _baseloop.TimerAdd(_next_id,delay,task);
    }

public:
    TcpServer(int port)
        :_port(port)
        ,_next_id(0)
        ,_enable_inactive_release(false)
        ,_acceptor(&_baseloop,port)
        ,_pool(&_baseloop)
    {
        _acceptor.SetAcceptCallback(std::bind(&TcpServer::NewConnection,this,std::placeholders::_1));
        _acceptor.Listen();//将监听套接字挂在baseloop上
    }
    //设置线程池数量
    void SetThreadCount(int count)
    {
        _pool.SetThreadCount(count);
    }
    void SetConnectedCallback(const ConnectedCallback&cb)
    {
        _connected_callback = cb;
    }
    void SetMessageCallback(const MessageCallback&cb)
    {
        _message_callback = cb;
    }
    void SetClosedCallback(const ClosedCallback&cb)
    {
        _closed_callback = cb;
    } 
    void SetAnyEventCallback(const AnyEventCallback&cb)
    {
        _event_callback = cb;
    }
    //添加定时任务
    void RunAfter(const Functor task,int delay)
    {
        _baseloop.RunInLoop(std::bind(&TcpServer::RunAfterInLoop,this,task,delay));
    }
    //启动非活跃销毁
    void EnableInactiveRelease(int timeout)
    {
        _timeout = timeout;
        _enable_inactive_release = true;
    }
    void Start()
    {
        _pool.Create();
        _baseloop.Start();
    }
};
相关推荐
AI_567820 小时前
Selenium+Python可通过 元素定位→操作模拟→断言验证 三步实现Web自动化测试
服务器·人工智能·python
ShineWinsu21 小时前
对于C++:类和对象的解析—下(第二部分)
c++·面试·笔试·对象··工作·stati
model200521 小时前
alibaba linux3 系统盘清理
linux·运维·服务器
WG_1721 小时前
Linux:动态库加载总结_进程间通信+进程池 + 进程IPC(27/28/29/30/31/32)
linux·运维·服务器
BHXDML1 天前
第七章:类与对象(c++)
开发语言·c++
yyf198905251 天前
C++ 跨平台开发的挑战与应对策略
c++
Root_Hacker1 天前
include文件包含个人笔记及c底层调试
android·linux·服务器·c语言·笔记·安全·php
REDcker1 天前
RESTful API设计规范详解
服务器·后端·接口·api·restful·博客·后端开发
又见野草1 天前
C++类和对象(中)
开发语言·c++
微学AI1 天前
内网穿透的应用-告别局域网束缚!MonkeyCode+cpolar 解锁 AI 编程新体验
linux·服务器·网络