从零手写高性能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主要做两件事:
-
若开启了非活跃销毁,调用 _loop->TimerRefresh(_conn_id)刷新定时器,延长连接存活时间。
-
调用用户注入的任意事件回调。
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