从零手写高性能C++ TCP 服务器框架(十一) --- Connection实现

从零手写高性能C++ TCP 服务器框架(十一) --- Connection实现

一、前置知识

在实现 Connection 模块之前,我们已经完成了 EventLoop、Channel、Poller、Buffer、Socket、TimerWheel 等基础组件的封装。Connection 模块将这些组件有机地结合起来,对一条 TCP 连接进行全生命周期的管理。理解 Connection 需要提前了解以下几个核心概念:

1. 连接的状态变迁

一条 TCP 连接从建立到关闭会经历多个状态,Connection 内部通过枚举 ConStatus 定义了四个状态:

  • DISCONNECTED:连接已关闭或尚未建立

  • CONNECTING :连接刚刚建立,还未完成内部设置(如 Channel 回调绑定)

  • CONNECTED: 连接已完全就绪,可以正常通信

  • DISCONNECTING:连接正在关闭过程中(半关闭或等待缓冲区数据发送完毕)

2. 缓冲区的作用

非阻塞 IO 下,单次 recv / send 不一定能读完或发完所有数据,Buffer 类提供输入/输出缓冲区,Connection 在读事件中持续将数据放入_in_buffer,在写事件中将 _out_buffer 中的数据逐步发出,直到缓冲区清空才关闭写事件监听。

3. Channel 的回调机制

每个连接关联一个 Channel 对象,通过设置可读、可写、关闭、错误等回调,将底层 epoll 事件转化为 Connection 的成员函数调用(如 HandleWrite、HandleRead )。

4. 线程安全模型

EventLoop 提供了 RunInLoop 方法,确保所有对连接的操作都在 I/O 线程中执行。Connection 的所有公开接口 (Send、Shutdown 等)都通过 RunInLoop 将实际工作投递到所属 EventLoop 的线程中,内部方法以 InLoop 为后缀。


二、Connection 模块设计

2.1 设计目标

Connection 是对一条 TCP 连接的完整封装,提供以下管理能力:

  • 套接字管理:封装 Socket对象,进行非阻塞读写和关闭

  • 事件管理:通过 Channel监听可读、可写、错误、挂断等事件

  • 缓冲区管理:维护输入缓冲区和输出缓冲区,支持数据暂存与异步发送

  • 上下文管理:通过 Any 类型存储任意用户自定义上下文,支持协议切换

  • 回调管理:提供连接建立、消息到达、连接关闭、任意事件四种回调,由上层业务注入逻辑

2.2 核心成员变量

cpp 复制代码
uint64_t         _conn_id;                // 连接唯一ID,同时用于定时器ID
int              _sockfd;                 // 文件描述符
bool             _enable_inactive_release; // 是否启用了非活跃超时销毁
EventLoop       *_loop;                  // 所属事件循环
ConStatus        _status;                // 连接当前状态
Socket           _socket;                // 套接字操作对象
std::shared_ptr<Channel> _channel;       // 事件通道
Buffer           _in_buffer;             // 输入缓冲区
Buffer           _out_buffer;            // 输出缓冲区
Any              _context;               // 业务上下文

// 用户注入的回调函数
ConnectedCallback _connected_callback;   // 连接建立完成
MessageCallback   _message_callback;     // 收到数据
ClosedCallback    _closed_callback;      // 连接关闭
AnyEventCallback  __event_callback;      // 任意事件
ClosedCallback    _server_closed_callback; // 从上层管理结构中移除连接

2.3 公开接口说明

接口 描述
Established() 连接建立后调用,在 I/O 线程中设置 Channel 回调并触发 _connected_callback
Send(data,len) 向对端发送数据,将数据放入输出缓冲区并激活写事件
ShutDown() 优雅关闭连接,关闭写端并等待数据发送完毕后释放
EnableInactiveRelease(int sec) 启用非活跃超时销毁,秒数内无通信则自动关闭
CancelInactiveRelease() 取消已启用的非活跃超时定时器
Upgrade(context, ...) 协议切换,重新设置上下文和四个业务回调函数
SetContext/GetContext 存取用户自定义上下文
SetXxxCallback 分别设置连接建立、消息、关闭、任意事件回调

所有公开接口均线程安全:若调用者不在 I/O 线程,则通过 _loop->RunInLoop 投递任务。

2.4 状态迁移

复制代码
[新连接] → CONNECTING → CONNECTED → DISCONNECTING → DISCONNECTED
  • CONNECTINIG:accept 得到 fd 后,Connection 构造即进入此状态,等待 Established() 设置回调并启动监听。

  • CONNECTED: Established() 完成后进入,此时可以正常收发数据。

  • DISCONNECTING:用户调用 ShutDown() 或发生内部错误时进入,等待缓冲数据发送完毕后释放。

  • DISCONNECTED:连接彻底释放完成。

2.5. 相关接口

cpp 复制代码
#ifndef __CONNECTION__
#define __CONNECTION__

#include <cstdint>
#include <functional>
#include "socket.hpp"
#include "channel.hpp"
#include "eventLoop.hpp"
#include "buffer.hpp"
#include "any.hpp"
#include "log.hpp"

namespace xxhh
{
    class Connection;

    // 连接状态枚举
    typedef enum
    {
        DISCONNECTED, // 连接关闭状态
        CONNECTING,   // 连接建立成功,待处理状态
        CONNECTED,    // 连接建立完成,可以通信的状态
        DISCONNECTING // 待关闭状态
    } ConStatus;

    using PtrConnection = std::shared_ptr<Connection>;

    class Connection : public std::enable_shared_from_this<Connection>
    {
    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 &)>;

    private:
        /* 五个 channel 的事件回调函数 */
        // 描述符可读事件触发后调用的函数,接收 socket 数据放到接收缓冲区中,然后调用 _message_callback
        void HandleRead();
        // 描述符可写事件触发后调用的函数,将发送缓冲区中的数据发往对端
        void HandleWrite();
        // 描述符触发挂断事件
        void HandleClose();
        // 描述符触发出错事件
        void HandleError();
        // 描述符触发任意事件
        void HandleEvent();

        // 连接获取之后,所处的状态下要进行各种设置(给 channel 设置事件回调,启动读事件)
        void EstablishInLoop();
        // 真正的释放接口
        void ReleaseInLoop();
        // 把数据放到发送缓冲区,启动可写事件监控(并非实际发送)
        void SendInLoop(Buffer &buf);
        // 非实际连接释放操作,判断还有没有数据待处理、待发送
        void ShutdownInLoop();
        // 开启非活跃连接销毁定时
        void EnableInactiveReleaseInLoop(int sec);
        // 取消非活跃连接销毁定时
        void CancelInactiveReleaseInLoop();
        // 升级协议 - 重置上下文以及网络处理逻辑
        void UpgradeInLoop(const Any &context,
                           const ConnectedCallback &connect_cb,
                           const MessageCallback &message_cb,
                           const ClosedCallback &closed_cb,
                           const AnyEventCallback &event_cb);

    public:
        Connection(EventLoop *loop, uint64_t conn_id, int sockfd);
        ~Connection();

        // 获取管理的文件描述符
        int Fd();
        // 获取连接ID
        int Id();
        // 是否处于 Connected 状态
        bool Connected();

        // 设置上下文 - 连接建立完成后进行调用
        void SetContext(const Any &context);
        // 获取上下文
        Any *GetContext();

        // 设置各类回调函数
        void SetConnectedCallback(const ConnectedCallback &cb);
        void SetMessageCallback(const MessageCallback &cb);
        void SetClosedCallback(const ClosedCallback &cb);
        void SetAnyEventCallback(const AnyEventCallback &cb);
        void SetSrvClosedCallback(const ClosedCallback &cb);

        // 连接建立后,进行 channel 回调设置,可取消监听,调用 _connected_callback
        void Established();

        // 发送数据,将数据添加到发送缓冲区,启动写事件监控
        void Send(const char *data, size_t len);

        // 提供给组件使用者的关闭接口 - 并不实际关闭,需要外部自定义函数进行处理
        void ShutDown();

        // 启动非活跃连接自动释放(秒)
        void EnableInactiveRelease(int sec);
        // 取消非活跃连接释放
        void CancelInactiveRelease();

        // 切换协议 - 重置上下文以及网络处理逻辑
        void Upgrade(const Any &context,
                     const ConnectedCallback &connect_cb,
                     const MessageCallback &message_cb,
                     const ClosedCallback &closed_cb,
                     const AnyEventCallback &event_cb);

    private:
        uint64_t _conn_id;                     // 连接唯一ID
        int _sockfd;                           // 连接关联的文件描述符
        bool _enable_inactive_release;         // 是否启动非活跃销毁判断标志,默认 false
        EventLoop *_loop;                      // 所关联的事件循环
        ConStatus _status;                     // 连接状态
        Socket _socket;                        // 套接字操作管理
        std::shared_ptr<Channel> _channel;     // 连接的事件管理
        Buffer _in_buffer;                     // 输入缓冲区
        Buffer _out_buffer;                    // 输出缓冲区
        Any _context;                          // 请求接收处理上下文

        // 组件使用者回调
        ConnectedCallback _connected_callback;
        MessageCallback _message_callback;
        ClosedCallback _closed_callback;
        AnyEventCallback _event_callback;
        // 服务器内部用于移除连接信息的回调
        ClosedCallback _server_closed_callback;
    };
}

#endif // __CONNECTION__

三、关键实现细节

3.1 读事件处理

HandleRead 为非阻塞循环读取,将数据写入 _in_buffer。若读出错(ret < 0),不直接关闭连接,而是调用 ShutDown() 进入待关闭状态,让缓冲区内数据有机会通过消息回调交给业务层处理。读成功后,若缓冲区有数据,则调用_message_callback(shared_from_this(), &_in_buffer),将缓冲区指针传递给用户,用户从中直接取数据。

3.2 写事件处理

HandleWrite 从 _out_buffer取数据发送。若发送出错,先将输入缓冲区剩余数据交给上层(防止数据丢失),然后彻底释放。若发完所有数据且状态为 DISCONNECTING,则直接释放连接;若状态正常,则关闭写事件监听。

3.3 挂断与错误处理

对端挂断 (HandleClose) 时,先将输入缓冲区剩余数据通过 _message_callback 交给业务,然后彻底释放。错误事件直接转到 HandleClose 处理。

3.4 任意事件处理

HandleEvent主要做两件事:

  1. 若开启了非活跃销毁,调用 _loop->TimerRefresh(_conn_id)刷新定时器,延长连接存活时间。

  2. 调用用户注入的任意事件回调。

3.5 连接建立流程

Establish() 由TcpServer 连接创建后调用,内部通过 _loop->RunInLoop 调 EstablishInLoop:

  • 将状态从 CONNECTING 改为 CONNECTED

  • 启动读监听

  • 执行 _connected_callback

3.6 优雅关闭

Shutdown() 调用 ShutdownInLoop:

  • 状态置为 DISCONNECTING

  • 若有接收缓冲区数据,通知业务处理

  • 若有待发数据,开启写事件,等待数据全部发送完毕后自动释放

  • 若无待发数据,直接释放

3.7 非活跃超时销毁

EnableInactiveRelease(sec) 调用 EnableInactiveReleaseInLoop:

  • 若已存在定时器则刷新,否则新增一个定时任务std::bind(&Connection::ReleaseInLoop, this)

  • 每次任意事件(如读写)会刷新定时器,从而实现"空闲超过 sec 秒自动关闭"

CancelInactiveRelease() 取消定时器并重置标志。

3.8 协议切换

Upgrade 允许在一条连接上动态替换上下文和四个回调(如 HTTP 升级 WebSocket)。为确保切换立即生效,该接口要求调用者在 I/O 线程内调用 (_loop->AssertInLoop()),然后通过 RunInLoop执行 UpgradeInLoop,避免新数据到达时仍使用旧回调。


四、代码实现

cpp 复制代码
#ifndef __CONNECTION__
#define __CONNECTION__
#include <cstdint>
#include <functional>
#include "socket.hpp"
#include "channel.hpp"
#include "eventLoop.hpp"
#include "buffer.hpp"
#include "any.hpp"
#include "log.hpp"
namespace xxhh
{
    class Connection;
    typedef enum
    {
        DISCONNECTED, // disconnected - 连接关闭状态
        CONNECTING,   // connecting - 连接建立成功,待处理状态
        CONNECTED,    // connected - 连接建立完成,各种设置已完成,可以通信的状态
        DISCONNECTING // disconnecting - 待关闭状态
    } ConStatus;
    using PtrConnection = std::shared_ptr<Connection>;
    class Connection : public std::enable_shared_from_this<Connection>
    {
    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 &)>;

    private:
        /* 五个 channel 的事件回调函数 */
        // 描述符可读事件触发后调用的函数,接收 socket 数据放到接收缓冲区中,然后调用 _message_callback
        void HandleRead()
        {
            // 1. 接受 socket 的数据,放入到缓冲区里面
            char buffer[65536];
            int ret = _socket.NonBlockRecv(buffer, sizeof(buffer) - 1);
            if (ret < 0)
            { // 出错了,不能直接关闭连接
                // 可能有缓冲区需要处理
                return ShutDown();
            }
            // ret = 0 表示没有收到数据,而不是连接断开,连接断开断开返回的事 -1
            _in_buffer.WriteAndPush(buffer, ret);
            // 2. 调用 _message_callback 进行业务处理
            if (_in_buffer.ReadAbleSize() > 0 && _message_callback)
                return _message_callback(shared_from_this(), &_in_buffer);
        }

        // 描述符可写事件触发后调用的函数,将发送缓冲区中的数据发往发送
        void HandleWrite()
        {
            // _out_buffer 中保存的要发送的数据
            ssize_t ret = _socket.NonBlockSend(_out_buffer.ReadPosition(), _out_buffer.ReadAbleSize());
            if (ret < 0)
            {
                // 发送错误了,就需要关闭连接
                // 首先是对 缓冲区里面的数据进行业务处理
                if (_in_buffer.ReadAbleSize() > 0 && _message_callback)
                    return _message_callback(shared_from_this(), &_in_buffer);
            }
            // 读偏移向后移动
            _out_buffer.MoveReadOffset(ret);
            if (_out_buffer.ReadAbleSize() == 0)
            {
                _channel->DisableWrite(); // 没有发送数据,就关闭写事件监控
                // 如果当前处于连接待关闭状态、则有数据,发送完数据释放连接,没有数据则直接释放
                if (_status == DISCONNECTING)
                    return ReleaseInLoop();
            }
            return;
        }
        // 描述符触发挂断事件
        void HandleClose()
        {
            // 一旦连接挂断了,套接字什么就不需要干,因此有数据待处理就处理一下,完毕后关闭连接
            if (_in_buffer.ReadAbleSize() > 0 && _message_callback)
                return _message_callback(shared_from_this(), &_in_buffer);
            return ReleaseInLoop();
        }
        // 描述符触发出错事件
        void HandleError()
        {
            return HandleClose();
        }
        // 描述符触发任意事件

        void HandleEvent()
        {
            // 1. 刷新连接的活跃度 -- 延迟定时销毁任务
            if (_enable_inactive_release == true)
                _loop->TimerRefresh(_conn_id);
            // 2. 调用组件使用者的任意事件回调
            if (_event_callback)
                return _event_callback(shared_from_this());
        }
        // 连接获取之后,所处的状态下要进行各种设置(给 channel 设置事件回调,启动读事件)
        void EstablishInLoop()
        {
            // 1. 修改连接状态
            assert(_status == CONNECTING); // 当前状态必须是 半连接状态
            _status = CONNECTED;
            // 2. 启动读事件监控
            _channel->EnableRead();
            // 3. 调用回调函数
            if (_connected_callback)
                _connected_callback(shared_from_this());
        }
        // 这个接口才是真正的释放接口
        void ReleaseInLoop()
        {
            // 1. 修改连接状态,将其置为 DISCONNECTED
            _status = DISCONNECTED;
            // 2. 移除连接的事件监控
            _channel->Remove();
            // 3. 关闭文件描述符
            _socket.Close();
            // 4. 如果当前定时器队列中还有定时任务,那么就销毁
            if (_loop->HasTimer(_conn_id))
                CancelInactiveReleaseInLoop();
            // 5. 调用关闭回调函数,避免先移除服务器管理的连接信息导致 Connection 被释放,
            // 再去处理就会出错,因此先调用用户的关闭回调函数
            if (_closed_callback)
                _closed_callback(shared_from_this());
            // 6. 移除服务器内部管理的连接信息
            if (_server_closed_callback)
                _server_closed_callback(shared_from_this());
        }
        // 这个接口并不是实际的发送接口,而是把数据放到发送缓冲区,启动可写事件监控
        void SendInLoop(Buffer &buf)
        {
            if (_status == DISCONNECTED)
                return;
            _out_buffer.WriteBufferAndPush(buf);
            if (_channel->WriteAble() == false)
            {
                _channel->EnableWrite();
            }
        }
        // 这个关闭接口并非实际的连接释放操作,需要判断还有没有数据待处理,待发送
        void ShutdownInLoop()
        {
            _status = DISCONNECTING;
            if (_in_buffer.ReadAbleSize() > 0)
            {
                if (_message_callback)
                    _message_callback(shared_from_this(), &_in_buffer);
            }
            // 要么就是写入数据的时候出错关闭,要么就是没有待发送数据,直接关闭
            if (_out_buffer.ReadAbleSize() > 0)
            {
                if (_channel->WriteAble() == false)
                    _channel->EnableWrite();
            }
            if (_out_buffer.ReadAbleSize() == 0)
                ReleaseInLoop();
        }
        void EnableInactiveReleaseInLoop(int sec)
        {
            // 1. 将判断标志 _enable_inactive_release 置为 true
            _enable_inactive_release = true;
            // 2. 如果当前定时销毁任务已经存在, 那就刷新延迟一下即可
            if (_loop->HasTimer(_conn_id))
                return _loop->TimerRefresh(sec);
            // 3. 如果不存在定时销毁任务,则新增
            _loop->TimerAdd(_conn_id, sec, std::bind(&Connection::ReleaseInLoop, this));
        }

        void CancelInactiveReleaseInLoop()
        {
            _enable_inactive_release = false;
            if (_loop->HasTimer(_conn_id))
                _loop->TimerCancel(_conn_id);
        }
        void UpgradeInLoop(const Any &context,
                           const ConnectedCallback &connect_cb, const MessageCallback &message_cb,
                           const ClosedCallback &closed_cb, const AnyEventCallback &event_cb)
        {
            _context = context;
            _connected_callback = connect_cb;
            _message_callback = message_cb;
            _closed_callback = closed_cb;
            _event_callback = event_cb;
        }

    public:
        Connection(EventLoop *loop, uint64_t conn_id, int sockfd)
            : _conn_id(conn_id), _sockfd(sockfd), _enable_inactive_release(false),
              _loop(loop), _status(CONNECTING), _socket(_sockfd),
              _channel(std::make_shared<Channel>(loop, _sockfd))
        {
            _channel->SetReadCallBack(std::bind(&Connection::HandleRead, this));
            _channel->SetWriteCallBack(std::bind(&Connection::HandleWrite, this));
            _channel->SetCloseCallBack(std::bind(&Connection::HandleClose, this));
            _channel->SetErrorCallBack(std::bind(&Connection::HandleError, this));
            _channel->SetEventCallBack(std::bind(&Connection::HandleEvent, this));
        }
        ~Connection()
        {
            DBG_LOG("Release Connection:%p", this);
        }
        int Fd() // 获取管理的文件描述符
        {
            return _sockfd;
        }

        int Id() // 获取连接ID
        {
            return _conn_id;
        }
        bool Connected() // 是否处于 Connected 状态
        {
            return _status == CONNECTED;
        }
        // 设置上下文 - 连接建立完成后进行调用
        void SetContext(const Any &context)
        {
            _context = context;
        }
        Any *GetContext() // 获取上下文
        {
            return &_context;
        }
        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 SetSrvClosedCallback(const ClosedCallback &cb)
        {
            _server_closed_callback = cb;
        }
        // 连接建立后,进行channel回调设置,可取消监听,调用 _connected_callback
        void Established()
        {
            _loop->RunInLoop(std::bind(&Connection::EstablishInLoop, this));
        }
        // 发送数据,将数据添加到发送缓冲区,启动与事件循环
        void Send(const char *data, size_t len)
        {
            // 外界传入的 data, 可能是个临时的空间, 我们现在只是把发送操作压入到任务池,有可能并没有被立即执行
            // 因此有可能执行的时候, data 指向的空间有可能已经被释放了
            Buffer buf;
            buf.WriteAndPush(data, len);
            _loop->RunInLoop(std::bind(&Connection::SendInLoop, this, std::move(buf)));
        }
        // 提供给组件使用者的关闭接口 - 并不实际关闭,需要外部自定义函数进行处理
        void ShutDown()
        {
            _loop->RunInLoop(std::bind(&Connection::ShutdownInLoop, this));
        }
        // 自动重连机制,并定义多长时间无通信就重启连接,添加定时任务
        void EnableInactiveRelease(int sec)
        {
            _loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop, this, sec));
        }
        // 取消非活跃链接
        void CancelInactiveRelease()
        {
            _loop->RunInLoop(std::bind(&Connection::CancelInactiveReleaseInLoop, this));
        }

        // 切换协议 - 重置上下文以及网络处理逻辑
        void Upgrade(const Any &context,
                     const ConnectedCallback &connect_cb, const MessageCallback &message_cb,
                     const ClosedCallback &closed_cb, const AnyEventCallback &event_cb)
        {
            _loop->AssertInLoop();
            _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this,
                                       context, connect_cb, message_cb, closed_cb, event_cb));
        }

    private:
        // 连接的唯一ID、便于连接的管理和查找
        uint64_t _conn_id;
        // uint64_t _timer_id; // 定时器ID,必须是唯一的,这块为了简化操作使用 _conn_id 定时器
        int _sockfd;                       // 连接关联的文件描述符
        bool _enable_inactive_release;     // 连接是否启动非活跃销毁的判断表示。默认为 false
        EventLoop *_loop;                  // 连接所关联的一个 EventLoop
        ConStatus _status;                 // 连接状态
        Socket _socket;                    // 套接字操作管理
        std::shared_ptr<Channel> _channel; // 连接的事件管理
        Buffer _in_buffer;                 // 输入缓冲区 -- 存放从 socket 中读取的数据
        Buffer _out_buffer;                // 输出缓冲区 -- 存放要发送给对端的数据
        Any _context;                      // 请求接收处理上下文

        // 这几个回调函数都是组件使用者使用的
        ConnectedCallback _connected_callback;
        MessageCallback _message_callback;
        ClosedCallback _closed_callback;
        AnyEventCallback _event_callback;
        // 一旦某个连接要关闭,就应该从管理的地方移除自己的信息
        ClosedCallback _server_closed_callback;
    };
}
#endif

五、测试代码编写

服务器端:

cpp 复制代码
#include <memory>
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <functional>
#include "../source/socket.hpp"
#include "../source/channel.hpp"
#include "../source/poller.hpp"
#include "../source/connection.hpp"

using namespace xxhh;

void HandleClose(std::shared_ptr<Channel> channel)
{
    std::cout << "close: " << channel->Fd() << std::endl;
    channel->Remove(); // 仅从 epoll 移除,不 delete
    // channel 的 shared_ptr 会自动释放,无需额外操作
}

void HandleRead(std::shared_ptr<Channel> channel)
{
    int fd = channel->Fd();
    char buf[1024] = {0};
    int ret = recv(fd, buf, sizeof(buf) - 1, 0);
    if (ret <= 0)
    {
        HandleClose(channel);
        return;
    }
    std::cout << "recv: " << buf << std::endl;
    channel->EnableWrite(); // 启动可写事件
}

void HandleWrite(std::shared_ptr<Channel> channel)
{
    int fd = channel->Fd();
    const char *msg = "天气好";
    int ret = send(fd, msg, strlen(msg), 0);
    if (ret < 0)
    {
        HandleClose(channel);
        return;
    }
    channel->DisableWrite(); // 写完关闭写监控
}

void HandleError(std::shared_ptr<Channel> channel)
{
    HandleClose(channel);
}

void HandleEvent(EventLoop *loop, std::shared_ptr<Channel> channel, uint64_t timeid)
{
    loop->TimerRefresh(timeid);
}
std::unordered_map<uint64_t, PtrConnection> _conns;
uint64_t conn_id = 0;
void ConnectionDestory(const PtrConnection &conn)
{
    _conns.erase(conn->Fd());
}
void OnConnected(const PtrConnection &conn)
{
    DBG_LOG("New Connection:%p", conn.get());
}
void OnMessage(const PtrConnection &conn, Buffer *buf)
{
    DBG_LOG("%s", buf->ReadPosition());
    buf->MoveReadOffset(buf->ReadAbleSize());
    std::string str = "Hello world";
    conn->Send(str.c_str(), str.size());
    // conn->ShutDown();
}
// 连接接收器
void Acceptor(EventLoop *loop, std::shared_ptr<Channel> listen_channel)
{
    int fd = listen_channel->Fd();
    int newFd = accept(fd, nullptr, nullptr);
    if (newFd < 0)
        return;

    conn_id++;
    PtrConnection conn(new Connection(loop, conn_id, newFd));
    conn->SetMessageCallback(std::bind(OnMessage, std::placeholders::_1, std::placeholders::_2));
    conn->SetSrvClosedCallback(std::bind(ConnectionDestory, std::placeholders::_1));
    conn->SetConnectedCallback(std::bind(OnConnected, std::placeholders::_1));
    conn->EnableInactiveRelease(10); // 启动非活跃超时销毁
    conn->Established();             // 就绪初始化
    _conns.insert(std::make_pair(conn_id, conn));
}

int main()
{
    srand(time(NULL));
    EventLoop loop;
    Socket tcpSocket;
    tcpSocket.CreateServer(8080);

    // 监听 Channel 必须用 shared_ptr 管理,否则回调捕获裸指针会悬空
    auto listenChannel = std::make_shared<Channel>(&loop, tcpSocket.Fd());

    // 设置 Accept 回调,捕获 loop 指针和 listenChannel 的 shared_ptr
    listenChannel->SetReadCallBack(
        [&loop](std::shared_ptr<Channel> ch)
        {
            Acceptor(&loop, ch);
        });

    listenChannel->EnableRead();

    loop.Start();

    tcpSocket.Close();
    return 0;
}

客户端不用改变

测试结果

服务器端

cpp 复制代码
xxhh@VM-0-2-ubuntu:~/gitee/cplusplus--tcp/test$ ./server 
[DBG][0x7f326b8ca740 11:15:45 server.cc:64] New Connection:0x55fd5111cc60
[DBG][0x7f326b8ca740 11:15:45 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:46 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:47 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:48 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:48 server.cc:64] New Connection:0x55fd5111f620
[DBG][0x7f326b8ca740 11:15:48 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:49 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:49 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:50 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:50 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:51 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:51 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:52 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:52 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:53 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:53 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:54 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:54 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:55 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:55 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:56 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:56 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:57 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:57 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:58 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:58 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:59 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:15:59 server.cc:68] Hello world
[DBG][0x7f326b8ca740 11:16:00 server.cc:68] Hello world
[ERR][0x7f326b8ca740 11:16:00 ../source/socket.hpp:105] Socket Recv Failed!
[DBG][0x7f326b8ca740 11:16:01 server.cc:68] Hello world
[ERR][0x7f326b8ca740 11:16:01 ../source/socket.hpp:105] Socket Recv Failed!
^C

客户端1

客户端2

相关推荐
努力的章鱼bro1 小时前
CUDA编程入门
c++·人工智能·cuda
独自破碎E1 小时前
GoCloud - 基于 WebSocket 的实时聊天应用
网络·websocket·网络协议
19岁开始学习1 小时前
计算机网络
网络·计算机网络
AOwhisky1 小时前
MySQL 学习笔记(第二期):SQL 语言之库表操作与数据类型
linux·运维·数据库·笔记·sql·学习·mysql
酉鬼女又兒1 小时前
零基础入门计算机网络物理层:核心任务、四大关键特性与全类型传输媒体(双绞线/同轴电缆/光纤/微波/红外线/可见光)完整详解
网络·网络协议·计算机网络·职场和发展·求职招聘
爱就是恒久忍耐1 小时前
Ubuntu解决pip3安装库提示This environment is externally managed的问题
linux·python·ubuntu
努力努力再努力wz1 小时前
【C++高阶数据结构系列】:跳表 SkipList 详解:多层索引、随机晋升与C++ 完整实现(附跳表实现的源码)
开发语言·数据结构·数据库·c++·redis·缓存·skiplist
卡梅德生物科技小能手1 小时前
卡梅德生物科技深度解析LAG3(淋巴细胞活化基因3)
网络·人工智能·经验分享
江屿风1 小时前
C++图的两种构建算法流食般投喂-竞赛编
开发语言·c++·笔记·算法·图论