仿Muduo的高并发服务器:基于Tcp协议的回显服务器

前面我们已经实现了对应的基础框架,本期我们就来写基于此之上的回显服务器

相关代码已经上传:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可

目录

TcpServer模块

设计思路

源码

EchoServer模块

实现

服务器运行


TcpServer模块

设计思路

Tcp模块是对过往模块的整合,通过TcpServer模块实例化的对象,可以非常简单的完成一个服务器的搭建管理:

  1. Acceptor对象,创建一个监听套接字

  2. EventLoop对象,baseLoop对象,实现对监听套接字的事件监控

  3. std::unordered_map<uint64_t, PtrConnection> _conns,实现对所有新建连接的管理

  4. LoopThreadPool对象,创建loop线程池,对新建连接进行事件监控及处理

功能

  1. 设置从属线程池数量

  2. 启动服务器

  3. 设置各种回调函数(连接建立完成、消息、关闭、任意),用户设置给TcpServer,TcpServer设置给获取的新连接

  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设置的消息回调

源码

TcpServer.hpp

cpp 复制代码
#pragma once
#include"Acceptor.hpp"
#include"EventLoop.hpp"
#include"Channel.hpp"
#include"LoopThreadPool.hpp"
#include"Connection.hpp"
namespace ImMuduo 
{
    class TcpServer
    {
        using ConnectionCallback=std::function<void(const ConnectionPtr&)>;
        using MessageCallback=std::function<void(const ConnectionPtr&,Buffer*)>;
        using ClosedComplete=std::function<void(const ConnectionPtr&)>;
        using AnyCallback=std::function<void(const ConnectionPtr&)>;

        ConnectionCallback ConnectedCallback_;//链接的回调函数
        MessageCallback MessageCallback_;//链接的消息回调函数
        ClosedComplete ClosedCompleteCallback_;//链接的关闭完成回调函数
        AnyCallback AnyEventCallback_;//链接的任意回调函数
        // 组件内的连接关闭回调 - 组件内设置的,因为服务器组件内会把所有的连接管理起来,一旦某个连接要关闭
        // 就应该从管理的地方移除掉自己的信息 
        ClosedComplete ServerClosedCompleteCallback_;//组件内的关闭完成回调函数
        public:
            TcpServer(int port);
            ~TcpServer();
            void SetLoopThreadCount(int thread_count);
            void start();
            void stop();
        public:
            //启用不活动链接的释放,并且定义多长时间无通信后释放
            void EnableInactiveRelease(int sec);
            //添加定时任务,用于检查不活动链接是否超时
            void RunAfter(double delay,const Functor& callback);
            //设置链接的回调函数
            void SetConnectedCallback(ConnectionCallback callback);
            //设置链接的消息回调函数
            void SetMessageCallback(MessageCallback callback);
            //设置链接的关闭完成回调函数
            void SetClosedCompleteCallback(ClosedComplete callback);
            //设置链接的任意回调函数
            void SetAnyEventCallback(AnyCallback callback);
        private:
            //为新连接构造connection对象管理
            void NewConnection(int connfd);
            //移除连接的connection对象管理信息
            void RemoveConnection(const ConnectionPtr& conn);
        private:
            int port_;//监听端口
            uint64_t conn_id_;//自动增长的ID
            bool enabled_inactive_release_;//是否启用连接超时释放
            EventLoop loop_;//主线程,负责监听事件处理(必须位于 acceptor_ 之前)
            LoopThreadPool threadpool_;//从属EventLoop的线程池
            Acceptor acceptor_;//监听套接字的管理对象
            std::unordered_map<uint64_t,ConnectionPtr> channels_;//保存所有对应的连接对象的shared_ptr映射表
    };
}   

TcpServer.cpp

cpp 复制代码
#include "TcpServer.hpp"
#include "Log.hpp"
#include <cassert>

namespace ImMuduo
{
    TcpServer::TcpServer(int port)
        : port_(port), conn_id_(0), enabled_inactive_release_(false),
          acceptor_(&loop_, port), threadpool_(&loop_)
    {
        acceptor_.setCallback([this](int connfd) {
            NewConnection(connfd);
        });
    }

    TcpServer::~TcpServer() {}

    void TcpServer::SetLoopThreadCount(int thread_count)
    {
        threadpool_.SetThreadNum(thread_count);
        threadpool_.CreateThreads();
    }

    void TcpServer::start()
    {
        loop_.Start();
    }

    void TcpServer::stop()
    {
        loop_.Stop();
    }

    void TcpServer::EnableInactiveRelease(int sec)
    {
        enabled_inactive_release_ = true;
        for (auto& kv : channels_)
        {
            kv.second->EnableInactiveRelease(sec);
        }
    }

    void TcpServer::RunAfter(double delay, const Functor& callback)
    {
        static uint64_t timer_id = 0;
        loop_.TimerAdd(++timer_id, static_cast<uint32_t>(delay), callback);
    }

    void TcpServer::SetConnectedCallback(ConnectionCallback callback)
    {
        ConnectedCallback_ = std::move(callback);
    }

    void TcpServer::SetMessageCallback(MessageCallback callback)
    {
        MessageCallback_ = std::move(callback);
    }

    void TcpServer::SetClosedCompleteCallback(ClosedComplete callback)
    {
        ClosedCompleteCallback_ = std::move(callback);
    }

    void TcpServer::SetAnyEventCallback(AnyCallback callback)
    {
        AnyEventCallback_ = std::move(callback);
    }

    void TcpServer::NewConnection(int connfd)
    {
        uint64_t id = ++conn_id_;
        EventLoop* worker = threadpool_.NextLoop();

        auto conn = std::make_shared<Connection>(
            connfd, id, worker, ConnectedCallback_);

        conn->SetMessageCallback(MessageCallback_);
        conn->SetClosedCompleteCallback(ClosedCompleteCallback_);
        conn->SetAnyEventCallback(AnyEventCallback_);
        conn->SetClosedCompleteCallback(ClosedCompleteCallback_);
        conn->SetServerClosedCompleteCallback(
            [this](const ConnectionPtr& c) { RemoveConnection(c); });

        if (enabled_inactive_release_)
        {
            conn->EnableInactiveRelease(10);
        }

        channels_[id] = conn;
        conn->Established();
    }

    void TcpServer::RemoveConnection(const ConnectionPtr& conn)
    {
        channels_.erase(conn->GetConnId());
    }
}

EchoServer模块

实现

EchoServer.hpp

cpp 复制代码
#pragma once
#include"TcpServer.hpp"
#include"Buffer.hpp"
namespace ImMuduo 
{
    class EchoServer
    {
        public:
            EchoServer(int port);
            ~EchoServer()=default;
            void Start();
            void Stop();
        private:
            void OnConnected(const ConnectionPtr& conn);
            void OnClosed(const ConnectionPtr& conn);
            void OnMessage(const ConnectionPtr& conn, Buffer* buf);
        private:
            TcpServer tcp_server_;
    };
}

EchoServer.cpp

cpp 复制代码
#include "EchoServer.hpp"
#include "Log.hpp"

namespace ImMuduo
{
    EchoServer::EchoServer(int port)
        : tcp_server_(port)
    {
        tcp_server_.SetConnectedCallback(
            [this](const ConnectionPtr& conn) { OnConnected(conn); });
        tcp_server_.SetMessageCallback(
            [this](const ConnectionPtr& conn, Buffer* buf) {
                OnMessage(conn, buf);
            });
        tcp_server_.SetClosedCompleteCallback(
            [this](const ConnectionPtr& conn) { OnClosed(conn); });
    }

    void EchoServer::Start()
    {
        tcp_server_.SetLoopThreadCount(2);
        tcp_server_.start();
    }

    void EchoServer::Stop()
    {
        tcp_server_.stop();
    }

    void EchoServer::OnConnected(const ConnectionPtr& conn)
    {
        INFO("EchoServer: new connection, fd=%d, connId=%lu",
             conn->GetFd(), conn->GetConnId());
    }

    void EchoServer::OnMessage(const ConnectionPtr& conn, Buffer* buf)
    {
        conn->Send(buf->ReadPos(), buf->ReadableSize());
        buf->Clear();
    }

    void EchoServer::OnClosed(const ConnectionPtr& conn)
    {
        INFO("EchoServer: connection closed, fd=%d, connId=%lu",
             conn->GetFd(), conn->GetConnId());
    }
}

服务器运行

cpp 复制代码
#include"EchoServer.hpp"
#include"Log.hpp"
using namespace ImMuduo;
int main()
{
    EchoServer echo_server(8888);
    echo_server.Start();
    return 0;
}

本期内容到这里结束了,喜欢请点个赞谢谢

封面图自取:

相关推荐
我不是外星人15 小时前
有了 Harness Engineering ,真的还需要研发工程师吗?
前端·后端·ai编程
candyTong15 小时前
RTK 技术原理:一次典型会话里,80% 上下文是怎么省下来的
javascript·后端·架构
Rust研习社17 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒18 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro18 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax19 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH19 小时前
Koa和Express的区别
后端
MariaH19 小时前
Koa框架的使用
后端
luckdewei20 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某21 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx