这里是阿川的博客,祝您变得更强 ✨ 个人主页:在线OJ的阿川
写在开头
现在您看到的是我的结论或想法 ,但在这背后凝结了大量的思考、经验和讨论


目录
1.项目介绍
实现这个库,是有一天在课上老师推荐了一本书叫做《Linux多线程服务端编程》,然后下来自主地了解一下,发现muduo库很好的实现了C++中的网络编程,所以为了可以锻炼并提升自身的C++多线程编程和工程化的能力,参考github上的muduo库这个开源项目做了一些简化,自己实现了一个支持高并发的网络通信组件。
项目成果:
- 通过实现的高并发服务器组件,可以简洁快速的完成一个高性能的服务器搭建。
- 通过组件内提供的不同应用层协议支持,也可以快速完成一个高性能应用服务器的搭建(当前项目中提供 HTTP 协议组件的支持)。
项目相关技术栈 :
这个项目作为一个基础的网络通信组件,是用纯 C++ 语言编写的,没有使用任何的第三方库。只是用到了 C++11 的线程库和多路复用的系统接口。
- epoll:对比 select、poll 效率更高,但是只有 unix 平台支持。
- std::thread:方便易用、可移植。
个人职责:
-
基于 Reactor 模式,使用 epoll 实现 IO 多路复用,封装 Poller 与 Channel 模块完成对大量文件描述符的事件监控与就绪事件分发
-
采用 "one loop per thread" 的主从 Reactor 模型,主线程负责接收新连接,从线程池负责已建立连接的读写处理,新连接通过轮询分配到从线程
-
封装 Buffer 缓冲区,解决 TCP 粘包/拆包问题,支持数据搬移与自动扩容
-
使用 eventfd + 任务队列实现跨线程通信,通过 RunInLoop 将操作封装为任务投递到目标线程执行,保证连接操作的线程安全,避免加锁开销
-
基于 timerfd + 时间轮算法,结合 shared_ptr / weak_ptr 管理定时任务生命周期,实现非活跃连接超时自动断开
-
封装 Connection 时使用 RAII 思想与 enable_shared_from_this,通过智能指针管理连接生命周期,避免内存泄漏与重复释放
-
在 TCP 框架之上扩展实现 HTTP/1.1 服务器,采用状态机解析请求(请求行→头部→正文),支持正则路由、静态资源服务、URL 编解码
-
编写多客户端测试程序验证服务可靠性:通过连续发送不完整请求验证粘包处理,通过并发多客户端长连接验证线程安全与超时销毁机制
项目时间 :
关于时间,其实我没有制定详细的规划。是因为我也是抱着学习的心态来完成这个项目。在开发中可能会遇到一些问题,解决问题也是我学习的过程,因此时间就不能确定下来。大概花了 2 个月来完成,从项目目标的确定,到项目原型图的敲定,到各个模块的拆分,再到最后的开发测试部署。
2.模块设计

模块设计主要分为SERVER模块和协议模块两大块,而SERVER模块和协议模块又分为多个小模块,其中详细功能如下:
SERVER模块:
| 模块名称 | 作用 |
|---|---|
| SERVER模块 | 实现Reactor模型的TCP服务器 |
| Buffer模块 | 是一个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能 |
| Socket 模块 | Socket 模块是对套接字操作封装的一个模块,主要实现的 socket 的各项操作 |
| Channel模块 | Channel 模块是对一个描述符需要进行的 IO 事件管理的模块,实现对描述符可读,可写,错误... 事件的管理操作,以及 Poller 模块对描述符进行 IO 事件监控就绪后,根据不同的事件,回调不同的处理函数功能 |
| Connection 模块 | onnection 模块是对 Buffer 模块,Socket 模块,Channel 模块的一个整体封装,实现了对一个通信套接字的整体的管理,每一个进行数据通信的套接字(也就是 accept 获取到的新连接)都会使用 Connection 进行管理 |
| Acceptor 模块 | Acceptor 模块是对 Socket 模块,Channel 模块的一个整体封装,实现了对一个监听套接字的整体的管理 |
| TimerQueue 模块 | 是实现固定时间定时任务的模块,可以理解就是要给定时任务管理器,向定时任务管理器中添加一个任务,任务将在固定时间后被执行,同时也可以通过刷新定时任务来延迟任务的执行。这个模块主要是对 Connection 对象的生命周期管理,对非活跃连接进行超时后的释放功能 |
| Poller 模块 | 是对 epoll 进行封装的一个模块,主要实现 epoll 的 IO 事件添加,修改,移除,获取活跃连接功能 |
| EventLoop 模块 | EventLoop 模块可以理解就是我们上边所说的 Reactor 模块,它是对 Poller 模块,TimerQueue 模块,Socket 模块的一个整体封装,进行所有描述符的事件监控 |
| TcpServer 模块 | 这个模块是一个整体 Tcp 服务器模块的封装,内部封装了 Acceptor 模块,EventLoop 模块作为主、EventLoopThreadPool 模块作为从 |
协议模块:
| 模块名称 | 作用 |
|---|---|
| 协议模块 | 对当前的 Reactor 模型服务器提供应用层协议支持 |
| Util 模块 | 这个模块是 HTTP 请求数据模块,用于保存 HTTP 请求数据被解析后的各项请求元素信息 |
| HttpResponse 模块 | 这个模块是 HTTP 响应数据模块,用于业务处理后设置并保存 HTTP 响应数据的的各项元素信息,最终会被按照 HTTP 协议响应格式组织成为响应信息发送给客户端 |
| Connection 模块 | onnection 模块是对 Buffer 模块,Socket 模块,Channel 模块的一个整体封装,实现了对一个通信套接字的整体的管理,每一个进行数据通信的套接字(也就是 accept 获取到的新连接)都会使用 Connection 进行管理 |
| HttpContext 模块 | 这个模块是一个 HTTP 请求接收的上下文模块 |
| HttpServer 模块 | 这个模块是最终给组件使用者提供的 HTTP 服务器模块了,用于以简单的接口实现 HTTP 服务器的搭建 |
项目分层 :

项目架构:
整个框架的核心架构是主从Reactor + one loop per thread ,具体如下:

项目主要流程:
- 在实例化 TcpServer 对象过程中,完成 BaseLoop 的设置,Acceptor 对象的实例化,以及 EventLoop 线程池的实例化,以及std::shared_ptr的 hash 表的实例化。
- 为 Acceptor 对象设置回调函数:获取到新连接后,为新连接构建 Connection 对象,设置 Connection 的各项回调,并使用 shared_ptr 进行管理,并添加到 hash 表中进行管理,并为 Connection 选择一个 EventLoop 线程,为 Connection 添加一个定时销毁任务,为 Connection 添加事件监控。
- 启动 BaseLoop。
- 通过 Poller 模块对当前模块管理内的所有描述符进行 IO 事件监控,有描述符事件就绪后,通过描述符对应的 Channel 进行事件处理。
- 所有就绪的描述符 IO 事件处理完毕后,对任务队列中的所有操作顺序进行执行。
- 由于 epoll 的事件监控,有可能会因为没有事件到来而持续阻塞,导致任务队列中的任务不能及时得到执行,因此创建了 eventfd,添加到 Poller 的事件监控中,用于实现每次向任务队列添加任务的时候,通过向 eventfd 写入数据来唤醒 epoll 的阻塞。
- 实现向 Channel 提供可读,可写,错误等不同事件的 IO 事件回调函数,然后将 Channel 和对应的描述符添加到 Poller 事件监控中。
- 当描述符在 Poller 模块中就绪了 IO 可读事件,则调用描述符对应 Channel 中保存的读事件处理函数,进行数据读取,将 socket 接收缓冲区全部读取到 Connection 管理的用户态接收缓冲区中。然后调用由组件使用者传入的新数据到来回调函数进行处理。
- 组件使用者进行数据的业务处理完毕后,通过 Connection 向使用者提供的数据发送接口,将数据写入 Connection 的发送缓冲区中。
- 启动描述符在 Poll 模块中的 IO 写事件监控,就绪后,调用 Channel 中保存的写事件处理函数,将发送缓冲区中的数据通过 Socket 进行面向系统的实际数据发送。
3.核心模块详解
基础设施层模块:
Buffer模块 :解决 TCP 粘包问题
- Buffer 是一个支持自动扩容的字节缓冲区,用来临时存放"从 socket 收到的数据"和"要发出去的数据"。
- 支持自动扩容:
cpp
void EnsureWriteSpace(uint64_t len) {
// 情况1:尾部空闲空间就够了,直接返回
if (TailIdleSize() >= len) { return; }
// 情况2:尾部不够,但加上头部空闲空间够了
// 把数据往前搬,腾出尾部空间
if (len <= TailIdleSize() + HeadIdleSize()) {
uint64_t rsz = ReadAbleSize(); // 保存当前数据大小
std::copy(ReadPosition(), ReadPosition() + rsz, Begin()); // 数据搬到开头
_reader_idx = 0; // 读指针归零
_writer_idx = rsz; // 写指针 = 数据大小
} else {
// 情况3:总体空间不够,只能扩容
_buffer.resize(_writer_idx + len);
}
}
- HTTP 协议是按行解析的,Buffer 提供了
GetLine():
cpp
char *FindCRLF() {
return (char*)memchr(ReadPosition(), '\n', ReadAbleSize()); // 找换行符
}
std::string GetLine() {
char *pos = FindCRLF();
if (pos == NULL) return ""; // 没找到换行,说明数据不完整
return ReadAsString(pos - ReadPosition() + 1); // +1 把换行符也读出来
}
Socket模块 :套接字的 RAII封装
- Socket 类是对 Linux socket API 的 RAII 封装 ,把零散的
socket/bind/listen/accept/recv/send收拢成一个对象。 - 构造时获取 fd,析构时自动 close:
cpp
class Socket {
int _sockfd;
public:
Socket(int fd): _sockfd(fd) {}
~Socket() { Close(); } // 析构自动关闭,绝不泄漏
void Close() {
if (_sockfd != -1) {
close(_sockfd);
_sockfd = -1;
}
}
};
- 关键API说明
cpp
// 创建服务端套接字的完整流程
bool CreateServer(uint16_t port, ...) {
Create(); // socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
NonBlock(); // fcntl 设为非阻塞
Bind(ip, port); // bind 绑定地址
Listen(); // listen 进入监听
ReuseAddress(); // 设置地址重用
return true;
}
- 非阻塞 IO 与错误处理
cpp
ssize_t Recv(void *buf, size_t len, int flag = 0) {
ssize_t ret = recv(_sockfd, buf, len, flag);
if (ret <= 0) {
// EAGAIN:非阻塞模式下缓冲区没数据了(正常现象)
// EINTR:被信号中断(重试即可)
if (errno == EAGAIN || errno == EINTR) {
return 0; // 返回 0 表示"这次没读到",不是错误
}
return -1; // 真正的错误
}
return ret;
}
Timerwheel模块 :时间轮定时器
- TimerWheel 是一个基于 时间轮算法 的定时器,配合 timerfd,用来实现"非活跃连接超时自动断开"。
timerfd:
cpp
static int CreateTimerfd() {
int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // 创建定时器 fd
struct itimerspec itime;
itime.it_value.tv_sec = 1; // 首次 1 秒后触发
itime.it_interval.tv_sec = 1; // 之后每 1 秒触发一次
timerfd_settime(timerfd, 0, &itime, NULL);
return timerfd;
}
- shared_ptr / weak_ptr 的精妙配合:
cpp
class TimerWheel {
std::vector<std::vector<PtrTask>> _wheel; // 轮子,存 shared_ptr
std::unordered_map<uint64_t, WeakTask> _timers; // 映射表,存 weak_ptr
};
- 刷新定时器:
cpp
void TimerRefreshInLoop(uint64_t id) {
auto it = _timers.find(id);
PtrTask pt = it->second.lock(); // weak_ptr.lock() 提升为 shared_ptr
int delay = pt->DelayTime();
int pos = (_tick + delay) % _capacity;
_wheel[pos].push_back(pt); // 重新放回轮子(延迟执行)
}
- 析构即执行:
cpp
class TimerTask {
bool _canceled;
TaskFunc _task_cb;
ReleaseFunc _release;
public:
~TimerTask() { // 析构时自动执行!
if (_canceled == false) _task_cb(); // 没被取消就执行任务
_release(); // 从映射表中移除自己
}
void Cancel() { _canceled = true; } // 取消任务
};
核心层模块:
Channel模块与poller模块 :事件监控
- Channel 把"一个 fd、它关心的事件、事件触发时该执行的回调"这三者打包到一起。
cpp
class Channel {
int _fd; // 监控的 fd(但不拥有它)
EventLoop *_loop; // 所属的 EventLoop
uint32_t _events; // 当前需要监控的事件(如 EPOLLIN)
uint32_t _revents; // 实际就绪的事件(epoll_wait 填充)
EventCallback _read_callback; // 可读回调
EventCallback _write_callback; // 可写回调
EventCallback _error_callback; // 错误回调
EventCallback _close_callback; // 连接关闭回调
EventCallback _event_callback; // 任意事件回调
};
- 事件处理顺序:
cpp
void HandleEvent() {
// 1. 处理可读事件(EPOLLRDHUP 是对端关闭,EPOLLPRI 是带外数据)
if ((_revents & EPOLLIN) || (_revents & EPOLLRDHUP) || (_revents & EPOLLPRI)) {
if (_read_callback) _read_callback();
}
// 2. 处理可写事件
if (_revents & EPOLLOUT) {
if (_write_callback) _write_callback();
}
// 3. 注意:下面用了 else if!错误和关闭只能处理一个
else if (_revents & EPOLLERR) {
if (_error_callback) _error_callback(); // 错误通常意味着要关闭连接
}
else if (_revents & EPOLLHUP) {
if (_close_callback) _close_callback();
}
// 4. 任意事件回调(比如刷新连接活跃度)
if (_event_callback) _event_callback();
}
- Poller 是对 epoll 三个核心 API(
epoll_create/epoll_ctl/epoll_wait)的封装,并维护一个map<fd, Channel*>用于查找。
cpp
class Poller {
int _epfd; // epoll 实例
struct epoll_event _evs[MAX_EPOLLEVENTS]; // 存就绪事件数组
std::unordered_map<int, Channel *> _channels; // fd → Channel 映射
public:
Poller() {
_epfd = epoll_create(MAX_EPOLLEVENTS); // 创建 epoll
}
void UpdateEvent(Channel *channel) {
bool ret = HasChannel(channel);
if (ret == false) {
_channels[channel->Fd()] = channel; // 不存在 → ADD
Update(channel, EPOLL_CTL_ADD);
} else {
Update(channel, EPOLL_CTL_MOD); // 已存在 → MOD
}
}
void Poll(std::vector<Channel*> *active) {
int nfds = epoll_wait(_epfd, _evs, MAX_EPOLLEVENTS, -1); // 阻塞等事件
for (int i = 0; i < nfds; i++) {
auto it = _channels.find(_evs[i].data.fd);
it->second->SetREvents(_evs[i].events); // 设置实际事件
active->push_back(it->second); // 收集就绪的 Channel
}
}
};
EventLoop模块 :事件循环
EventLoop是整个框架的核心调度器,每个线程拥有一个 EventLoop 实例。- EventLoop 的主循环有三步:
cpp
void Start() {
while(1) {
// 第1步:事件监控 ------ 阻塞等 epoll 就绪事件
std::vector<Channel *> actives;
_poller.Poll(&actives);
// 第2步:事件处理 ------ 处理所有就绪的事件
for (auto &channel : actives) {
channel->HandleEvent();
}
// 第3步:执行任务 ------ 处理其他线程投递过来的任务
RunAllTask();
}
}
- 跨线程操作的唯一入口
cpp
void RunInLoop(const Functor &cb) {
if (IsInLoop()) {
cb(); // 如果是本线程,立即执行
} else {
QueueInLoop(cb); // 如果是其他线程,投递到任务队列
}
}
- 跨线程唤醒机制
cpp
void QueueInLoop(const Functor &cb) {
{
std::unique_lock<std::mutex> _lock(_mutex);
_tasks.push_back(cb); // 任务塞进池子
}
WeakUpEventFd(); // 唤醒 EventLoop
}
void WeakUpEventFd() {
uint64_t val = 1;
write(_event_fd, &val, sizeof(val)); // 写入1,触发可读事件
}
- 任务池的 swap 技巧:最小化临界区
cpp
void RunAllTask() {
std::vector<Functor> functor;
{
std::unique_lock<std::mutex> _lock(_mutex);
_tasks.swap(functor); // 交换,瞬间完成
}
for (auto &f : functor) {
f(); // 不持锁地执行
}
}
连接层模块:
Concection模块 :连接的完整生命周期
- Connection 是一个连接的"大管家",封装了 socket、Channel、输入输出缓冲、各种回调,管理从连接建立到关闭的全过程。
- Connection 继承自
enable_shared_from_this<Connection>:
cpp
class Connection : public std::enable_shared_from_this<Connection> {
- Send 的异步设计
cpp
void Send(const char *data, size_t len) {
Buffer buf;
buf.WriteAndPush(data, len); // 先把数据拷贝到临时 Buffer
_loop->RunInLoop(std::bind(&SendInLoop, this, std::move(buf)));
}
服务层模块:
Acceptor模块:监听套接字
- Acceptor 封装了监听套接字,当有新连接到来时,调用回调把新 fd 交给上层。
cpp
class Acceptor {
Socket _socket; // 监听套接字
EventLoop *_loop;
Channel _channel; // 用于事件监控
AcceptCallback _accept_callback;
private:
void HandleRead() {
int newfd = _socket.Accept(); // 获取新连接
if (newfd < 0) return;
if (_accept_callback) _accept_callback(newfd); // 交给 TcpServer 处理
}
public:
Acceptor(EventLoop *loop, int port) {
// 构造时就创建了监听套接字(CreateServer 内部完成 socket/bind/listen)
_socket.CreateServer(port);
_channel.SetReadCallback(std::bind(&Acceptor::HandleRead, this));
}
void Listen() { _channel.EnableRead(); } // 启动读监控
};
- 从线程池:
cpp
class LoopThreadPool {
int _thread_count; // 线程数量
int _next_idx; // 下一个分配的索引
EventLoop *_baseloop; // 主线程的 loop(回退用)
std::vector<LoopThread*> _threads;
std::vector<EventLoop *> _loops; // 每个线程的 loop
public:
EventLoop *NextLoop() {
if (_thread_count == 0) return _baseloop; // 没有从线程,回退到主线程
_next_idx = (_next_idx + 1) % _thread_count; // 轮询
return _loops[_next_idx];
}
};
- 一个线程 + 一个 EventLoop:
cpp
class LoopThread {
std::mutex _mutex;
std::condition_variable _cond;
EventLoop *_loop;
std::thread _thread;
private:
void ThreadEntry() {
EventLoop loop; // 线程内实例化 EventLoop
{
std::unique_lock<std::mutex> lock(_mutex);
_loop = &loop;
_cond.notify_all(); // 通知 GetLoop 不再阻塞
}
loop.Start(); // 开始事件循环
}
public:
LoopThread():_loop(NULL), _thread(std::thread(&LoopThread::ThreadEntry, this)) {}
EventLoop *GetLoop() {
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [&](){ return _loop != NULL; }); // 等 loop 创建好
return _loop;
}
};
应用层模块:
EchoServer模块
- Echo Server 是对
TcpServer的最简封装,收到数据后原样返回
cpp
class EchoServer {
TcpServer _server;
void OnMessage(const PtrConnection &conn, Buffer *buf) {
conn->Send(buf->ReadPosition(), buf->ReadAbleSize());
buf->MoveReadOffset(buf->ReadAbleSize());
conn->Shutdown();
}
public:
EchoServer(int port) : _server(port) {
_server.SetThreadCount(2); // 2 个从线程
_server.EnableInactiveRelease(10); // 10 秒非活跃超时
// 设置回调...
}
};
HttpServer模块
- HTTP/1.1 请求解析(GET / POST / PUT / DELETE / HEAD)
- 状态机解析(请求行 → 头部 → 正文)
- 正则路由(URL 模式匹配 + 处理函数)
- 静态资源服务(根据扩展名返回 MIME 类型)
- URL 编解码(处理查询字符串中的特殊字符)
- 长短连接支持(Connection: keep-alive / close)
- HttpRequest / HttpResponse 数据结构
cpp
class HttpRequest {
std::string _method; // GET / POST ...
std::string _path; // /index.html
std::string _version; // HTTP/1.1
std::string _body; // 请求正文
std::unordered_map<std::string, std::string> _headers; // 头部字段
std::unordered_map<std::string, std::string> _params; // 查询字符串
};
class HttpResponse {
int _statu = 200; // 状态码
std::string _body; // 响应正文
std::unordered_map<std::string, std::string> _headers;
// 还支持重定向
bool _redirect_flag;
std::string _redirect_url;
};
- HttpContext:状态机解析
cpp
typedef enum {
RECV_HTTP_LINE, // 正在接收请求行
RECV_HTTP_HEAD, // 正在接收头部
RECV_HTTP_BODY, // 正在接收正文
RECV_HTTP_OVER, // 接收完成
RECV_HTTP_ERROR // 解析出错
} HttpRecvStatu;
class HttpContext {
HttpRecvStatu _recv_statu; // 当前状态
HttpRequest _request;
public:
void RecvHttpRequest(Buffer *buf) {
// 注意:这里没有 break!处理完一行立即继续处理头部
switch(_recv_statu) {
case RECV_HTTP_LINE: RecvHttpLine(buf); // 故意不 break
case RECV_HTTP_HEAD: RecvHttpHead(buf); // 故意不 break
case RECV_HTTP_BODY: RecvHttpBody(buf);
}
}
};
- 请求行解析用正则
cpp
bool ParseHttpLine(const std::string &line) {
// GET /login?user=xm&pass=123 HTTP/1.1
std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\?(.*))? (HTTP/1\.[01])");
std::regex_match(line, matches, e);
_request._method = matches[1]; // GET
_request._path = matches[2]; // /login
// matches[3] 是查询字符串 user=xm&pass=123
return true;
}
- 路由设计:正则 + Handler 表
cpp
class HttpServer {
using Handler = std::function<void(const HttpRequest &, HttpResponse *)>;
using Handlers = std::vector<std::pair<std::regex, Handler>>;
Handlers _get_route; // GET 请求的路由表
Handlers _post_route; // POST 请求的路由表
// ...
};
// 用户注册路由
server.Get("/hello", Hello); // 注册 GET /hello 的处理函数
server.Post("/login", Login);
// 路由匹配
void Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &handlers) {
for (auto &handler : handlers) {
if (std::regex_match(req._path, req._matches, handler.first)) {
handler.second(req, rsp); // 匹配成功,调用处理函数
return;
}
}
rsp->_statu = 404; // 都没匹配上
}
- 静态资源服务与安全防护
cpp
static std::string ExtMime(const std::string &filename) {
size_t pos = filename.find_last_of('.');
std::string ext = filename.substr(pos); // .html / .jpg / .css
return _mime_msg[ext]; // text/html / image/jpeg / text/css
}
- 路径穿越防护
cpp
static bool ValidPath(const std::string &path) {
// 按 / 分割路径,遇到 .. 深度减1,深度 < 0 说明试图跳出根目录
std::vector<std::string> subdir;
Split(path, "/", &subdir);
int level = 0;
for (auto &dir : subdir) {
if (dir == "..") {
level--;
if (level < 0) return false; // 跳出根目录,拒绝
} else {
level++;
}
}
return true;
}
4.项目测试
测试思路:
编写一个简单的 html 欢迎页面,然后使用浏览器进行访问
- 长连接连续请求测试:一个连接中每隔 3s 向服务器发送一个请求,查看是否会收到响应。
- 时连接释放测试:创建一个客户端,连接上服务器后,不进行消息发送,等待看超时后,连接是否会自动释放(当前默认设置超时时间为 10s)。预期结果:10s 后连接被释放。
- 数据中多条请求处理测试:给服务器发送的一条数据中包含有多个 HTTP 请求,观察服务器的处理。预期结果:每一条请求都有其对应的响应
- PUT 大文件上传测试:使用 put 请求上传一个大文件进行保存,大文件数据的接收会被分在多次请求中接收,然后计算源文件和上传后保存的文件的 MD5 值,判断请求的接收处理是否存在问题。(这里主要观察的是上下文的处理过程是否正常。)
5.困难挑战和心得体会
困难挑战:
- 跨线程操作的线程安全
最初不理解为什么要用任务队列而不是直接加锁,后来明白了 muduo 的核心思想:避免共享优于加锁。把操作投递到目标线程执行,既安全又高效。2.epoll 事件处理的顺序
HandleEvent 里错误和关闭事件为什么用 else-if,想了很久才理解是为了避免 double-free(一次事件循环只能关闭一次连接)。
心得体会:
学到了以项目为主导的开发应该如何完成,比如从项目确定到功能拆解,再到开发测试部署,从而独立完成项目的设计、实现、测试、部署
学到了当项目中遇到问题,迫使我思考和寻找解决方案,这增强了我的问题解决能力。
学到了如何进行有效的测试,以确保项目不出现 bug。例如功能测试,安全性测试等。
- 夹带私货
若你能看到看到这篇文章且能看到这 ,则说明你我有缘 ,留个关注吧 ,后面还会接着计算机408、底层原理、开源项目、以及数据、后端研发相关、实习、笔试/面试、秋招/春招、各种竞赛相关、简历相关、考研、学术相关......,祝你我变得更强
好的,到此为止啦,祝您变得更强

| 道阻且长 行则将至 |
|---|
个人主页:在线OJ的阿川 大佬的支持和鼓励,将是我成长路上最大的动力 ![]() |
