下面我按**“你贴出来的这篇博客 + 你两张类图” ,用初学者能跟着源码跑通的方式,把服务启动**这一章的知识讲透。你可以把它当成一张“从 main() 到 RtspSession::onRecv() 的全链路地图”。
0. 先用两张类图建立脑内模型
类图1(线程/事件循环/Server/TcpServer)
你要记住这条继承/组合链:
- TaskExecutorInterface:定义
async/sync这类“把任务丢到某线程执行”的接口 - TaskExecutor:实现了负载统计(靠 ThreadLoadCounter)
- EventPoller:一个 Reactor(一个线程 + 一个 epoll_wait 循环 + 任务队列)
- TaskExecutorGetterImp / EventPollerPool:管理多个 EventPoller,并按负载挑“最空闲的 poller”
- Server:只有一个核心成员
EventPoller::Ptr _poller(服务在哪个 poller 线程上运行) - TcpServer:真正的 TCP 监听/accept + 创建 Session + 连接分发
一句话:EventPollerPool 提供多条 IO 线程;TcpServer 把连接分摊到这些线程里。
类图2(Socket)
你要记住 Socket 体系的三点:
-
Socket 里最关键的是三类回调:
_on_before_accept:accept 前“先创建 peer Socket”(用于负载均衡挑 poller)_on_accept:accept 成功后“通知上层创建 Session”_on_read:读到数据后“把数据上抛给 Session::onRecv”
-
SockFD:封装 fd + sock 类型 + poller 指针
-
Socket::attachEvent() :把 peer fd 加进 poller 的 epoll,并开始触发 onRead/onWrite/onError
1. 服务启动全流程(按你博客的“服务创建流程”逐步解释)
Step 1:main.cpp 创建 TcpServer 并 start()
auto rtspSrv = std::make_shared<TcpServer>();
rtspSrv->start<RtspSession>(rtspPort);
关键点:start<SessionType> 决定了 accept 后创建什么 Session。
这里就是 RtspSession。
Step 2:TcpServer 构造 -> 顺带把 EventPollerPool 初始化了
TcpServer 构造会先构造父类 Server(poller):
Server::Server(EventPoller::Ptr poller) {
_poller = poller ? poller : EventPollerPool::Instance().getPoller();
}
这意味着:
- 你没传 poller 时,第一次用到 EventPollerPool::Instance() 就会触发线程池初始化
- pool 会创建 N 个 EventPoller(N≈CPU核数,或命令行
-t指定)
Step 3:EventPollerPool::addPoller() 做了什么?
它会循环创建 EventPoller("event poller i"),并为每个 poller 启一个线程跑 runLoop():
核心理解:
- 每个 EventPoller 对应一个线程
- 每个 EventPoller 在构造时创建 epoll fd,并把内部 pipe 的读端注册进 epoll(用于“唤醒线程执行任务”)
async()的实现本质是:把 task 放进队列 -> 往 pipe 写 1 字节 -> epoll_wait 被唤醒 -> 执行 task
这就是为什么 ZLToolKit 的“跨线程投递任务”很稳:不用锁住 epoll_wait,直接 pipe 唤醒。
Step 4:TcpServer::setOnCreateSocket(nullptr) 注册默认“造 Socket”的工厂
构造 TcpServer 时会做:
_on_create_socket = [](const EventPoller::Ptr &poller) {
return Socket::createSocket(poller, false);
};
意义很大:以后 TcpServer 要造任何 Socket(监听 socket / peer socket),都走这个回调。
注意 enable_mutex=false:设计目标是尽量让 socket 逻辑在所属 poller 单线程里跑,减少锁开销(高性能)。
Step 5:TcpServer::start() 注册 _session_alloc(Session 工厂)
start<SessionType> 的关键是这段:
auto session = std::make_shared<SessionType>(sock);session->setOnCreateSocket(server->_on_create_socket);return std::make_shared<SessionHelper>(server, session);
这里你要抓住三件事:
-
accept 之后,TcpServer 会用 _session_alloc 把 Socket 变成 Session(例如 RtspSession)
-
Session继承自SocketHelper,所以它也有_on_create_socket:- 它被设置成 TcpServer 的
_on_create_socket - 以后 Session 内部如果需要再创建 socket(比如主动连、代理等),会用这套工厂
- 它被设置成 TcpServer 的
-
SessionHelper会把 session 放进全局SessionMap,并且 TcpServer 也会把 helper 放进自己的_session_map做生命周期管理
记忆法: _session_alloc = “连接进来后,我应该创建什么会话对象”。
Step 6:TcpServer::start_l() -> setupEvent() 创建“监听 Socket”并挂回调
start_l()第一步就是 setupEvent():
6.1 创建监听 socket:绑定的是 Server::_poller
_socket = createSocket(_poller);
所以监听 socket 默认属于“创建这个 TcpServer 时拿到的 poller”。
6.2 挂两个回调(类图2 的核心)
setOnBeforeAccept(...)-> 设置 Socket::_on_before_acceptsetOnAccept(...)-> 设置 Socket::_on_accept
这两个回调就是 accept 链路的分水岭。
2. 连接进来后:从 listen fd 到 RtspSession::onRecv()
Step 7:listen() 会把监听 fd 加入 epoll,并把回调设为 Socket::onAccept()
Socket::listen(sock) 内部会调用:
_poller->addEvent(listen_fd, READ|ERROR, [this]{ onAccept(...); });
所以:epoll 触发 listen_fd 可读 = 有新连接 = 调 Socket::onAccept() 。
Step 8:Socket::onAccept() 的核心逻辑(你贴的代码非常关键)
它做的事情可以浓缩成 4 步:
8.1 accept() 拿到客户端 fd
fd = accept(listenfd, nullptr, nullptr);
8.2 创建 peer_sock(先负载均衡,失败再保底)
优先走:
peer_sock = _on_before_accept(_poller);
而你前面注册的 _on_before_accept 实际调用:
createSocket(EventPollerPool::Instance().getPoller(false));
也就是说:peer socket 会绑定“当前最空闲的 poller” 。
如果失败(一般不走)才保底:
peer_sock = Socket::createSocket(_poller, false); // 用父 poller
这就是你博客一直强调的:负载均衡分配 poller。
8.3 设置 peer fd(同时拿到对端 ip/port)
peer_sock->setPeerSock(fd) 会把 fd 封装成 SockFD 并挂到 peer_sock 上。
8.4 先触发 _on_accept,再 attachEvent(这个顺序很讲究)
代码里用 shared_ptr<void> completed 的析构来保证顺序:
- 先
_on_accept(peer_sock, completed):让上层创建 Session、设置 onRead/onErr - 当
_on_accept完成后,completed释放触发析构:执行attachEvent(peer_sock_fd) - attachEvent 才把 peer fd 加到 epoll,并开始收数据
目的:避免“还没设置 onRead 就收到数据事件” 。
Step 9:TcpServer::setupEvent() 里 _on_accept 做了啥?
你贴的逻辑很关键:
-
auto ptr = sock->getPoller().get(); -
auto server = strong_self->getServer(ptr);- 这一步会根据 poller 找到对应的 clone 出来的 TcpServer
-
ptr->async([server]{ server->onAcceptConnection(sock); });- 把真正的 session 创建动作,投递到 peer_sock 所属的 poller 线程执行
这一步解决了一个很关键的问题: “连接归哪个 poller,就在哪个线程里创建 session 和管理 session_map”。
Step 10:TcpServer::onAcceptConnection() -> 创建 Session + 绑定 onRead
onAcceptConnection 里两件事最重要:
-
调
_session_alloc(server, sock)创建RtspSession+SessionHelper -
设置
sock->setOnRead(...),回调里调用:strong_session->onRecv(buf);
最终就是RtspSession::onRecv()
所以完整链路是:
epoll(监听fd) -> Socket::onAccept() -> (负载均衡创建 peer socket) -> TcpServer::onAcceptConnection() -> sock->setOnRead -> RtspSession::onRecv
3. 为什么要 “clone 服务” 这一套?(你博客第10步)
start_l() 后半段会:
- 遍历 pool 里的所有 poller
- 给每个 poller 创建一个 TcpServer(
onCreatServer(poller)) cloneFrom(*this):在那个 poller 上重新 setupEvent,然后把监听 socket “clone”过去并 listen
你可以把它理解成:
- 每个 poller 上都有一个“同款 TcpServer”实例
- 这些 clone server 共享同一个 listen fd(底层通过 SockFD/SockNum 的复制共享)
- 然后每个 poller 都对 listen fd 执行
addEvent,这样多个线程都能等待 accept 事件
这就是经典的多线程 accept 模型(代码里 addEvent 使用了 epoll 的 EPOLLEXCLUSIVE,也是为了解决惊群/争抢问题)。
4. 初学者最容易混淆的 3 个点(我帮你拎出来)
混淆点 1:TcpServer::_on_create_socket vs Socket::_on_before_accept vs SocketHelper::_on_create_socket
TcpServer::_on_create_socket:TcpServer 的“造 Socket 工厂”(绑定 poller / 关 mutex)Socket::_on_before_accept:监听 socket 在 accept 前调用,用来“挑空闲 poller 创建 peer socket”SocketHelper::_on_create_socket:挂在 Session 上的“造 Socket 工厂”,一般用于 Session 内部需要再建连接时(默认继承父 poller,不做负载均衡逻辑)
混淆点 2:为什么 onAccept 里又 async 一次?
因为 accept 事件发生在“某个 poller 的监听 socket”上,但 peer socket 可能被分配到了另一个 poller。
所以必须把 onAcceptConnection() 投递到 peer socket 的 poller 线程执行,才能保证线程一致性。
混淆点 3:Timer(2s) 和 _is_on_manager 是干嘛的?
TcpServer 会定时 onManagerSession() 遍历 session 做维护。
遍历过程中如果同时发生 onErr 导致删除 session,会有迭代器失效风险,所以用 _is_on_manager + async erase 规避。
5. 你照着做就能“真正掌握”的 3 个练习
- 确认连接负载均衡是否生效
在TcpServer::onBeforeAcceptConnection()打断点,观察getPoller(false)每次返回的 poller 名称(event poller i)。 - 确认“先 onAccept 再 attachEvent”的顺序
在Socket::onAccept()里_on_accept(...)和attachEvent(...)各打一个 log,你会看到 attachEvent 总是后发生。 - 确认数据最终进入 RtspSession::onRecv()
在TcpServer::onAcceptConnection()的sock->setOnRead(...)回调里打断点,然后跟到RtspSession::onRecv()。
如果你想继续下一篇《推流创建》,你只要告诉我:你更关心 RTSP 推流(ANNOUNCE/RECORD) 还是 RTMP 推流(publish) ,我就按同样方式把“从 onRecv 解析协议 -> 创建 MediaSource -> 注册到全局 -> 开始收 RTP/FLV”这条链路教给你。
下面给你一份**“ZLMediaKit / ZLToolKit 服务启动(以 RTSP TcpServer 为例)”的详细流程**,从 main() 一路到 accept 新连接、创建 RtspSession、读到数据进入 RtspSession::onRecv() 。你可以把它当成读源码的路线图。
1) main.cpp:创建并启动 TcpServer
- 读取配置拿到
rtspPort auto rtspSrv = std::make_shared<TcpServer>();rtspSrv->start<RtspSession>(rtspPort);
关键:模板参数
RtspSession决定 accept 后创建的 Session 类型。
2) 构造 TcpServer:同时构造父类 Server,拿到主 poller
-
TcpServer::TcpServer(poller=nullptr) : Server(poller) { setOnCreateSocket(nullptr); } -
Server::Server(poller):-
如果传入
poller为空:- 调
EventPollerPool::Instance().getPoller()取一个 poller - 第一次调用 Instance() 会触发 EventPollerPool 初始化(创建线程池)
- 调
-
得到
Server::_poller
-
3) EventPollerPool 初始化:创建 N 个 EventPoller 线程(epoll线程)
-
EventPollerPool::EventPollerPool()->addPoller("event poller", size, ...) -
TaskExecutorGetterImp::addPoller(...):-
size = (传入size>0)?size:hardware_concurrency() -
循环 i=0..size-1:
new EventPoller("event poller i")poller->runLoop(false, register_thread)创建线程运行 epoll looppoller->async(...)设置线程名/CPU亲和性(可选)- 把 poller 放入
_threads
-
4) EventPoller 构造:创建 epoll + 内部管道事件
-
EventPoller::EventPoller(...):- 创建
_epoll_fd = epoll_create(...) - 建内部 pipe(用于唤醒 poller 执行 async 任务)
addEvent(pipe.readFD, READ, onPipeEvent)注册 pipe 读事件
- 创建
5) TcpServer 注册 “创建 Socket 的工厂回调” _on_create_socket
-
TcpServer::setOnCreateSocket(nullptr):-
cb 为空时设置默认:
_on_create_socket = [](poller){ return Socket::createSocket(poller, false); }
-
并同步到已 clone 的 server(若已有)
-
这个回调未来会用于:创建监听 Socket、创建客户端 peer Socket。
6) TcpServer::start:注册 Session 工厂 _session_alloc
-
TcpServer::start<RtspSession>(port, host, backlog):-
注册
_session_alloc = [](server, sock){ ... }auto session = std::make_shared<RtspSession>(sock)session->setOnCreateSocket(server->_on_create_socket)(Session继承SocketHelper)return std::make_shared<SessionHelper>(server, session)
-
调用
start_l(port, host, backlog)
-
7) start_l:创建监听 socket、listen、定时管理、clone 多个 server
TcpServer::start_l(...)做 4 件大事:
7.1 setupEvent:创建监听 Socket + 注册 accept 相关回调
setupEvent():
-
_socket = createSocket(_poller)createSocket(poller)其实就是_on_create_socket(poller)- 所以监听 socket 绑定
Server::_poller
-
注册 onBeforeAccept(给 Socket::_on_before_accept):
- 回调调用
TcpServer::onBeforeAcceptConnection(poller)
- 回调调用
-
注册 onAccept(给 Socket::_on_accept):
- 回调里根据
sock->getPoller()找到对应的 clone server ptr->async([server]{ server->onAcceptConnection(sock); })
- 回调里根据
7.2 listen:启动监听,并把 listen fd 加入 epoll
_socket->listen(port, host, backlog)
-
底层
SockUtil::listen()创建 listen fd 并 bind/listen -
Socket::listen(SockFD):_poller->addEvent(listen_fd, READ|ERROR, 回调)- 回调里调用
Socket::onAccept(...)
7.3 Timer:每 2 秒管理 Session
- 创建
_timer定期执行onManagerSession()(遍历 session 做维护)
7.4 clone:为每个 poller 创建一个 TcpServer(多线程监听/accept)
EventPollerPool::Instance().for_each(executor):
-
对每个 poller(除当前
_poller):- 若不存在:
serverRef = std::make_shared<TcpServer>(poller) serverRef->cloneFrom(*this)
- 若不存在:
cloneFrom(that):
-
setupEvent():在这个 poller 上创建自己的_socket并注册回调 -
_socket->cloneFromListenSocket(*(that._socket))- 复制 listen SockFD,并在本 poller 上执行
listen(sock)→addEvent(listen_fd, ...)
- 复制 listen SockFD,并在本 poller 上执行
-
设置 timer + 复制配置 + 记录 parent
效果:每个 poller 线程都对同一个 listen fd 做 epoll 监听,accept 事件可分摊到多线程。
8) 客户端连入时:accept → 分配 poller → 创建 RtspSession → 读数据进 onRecv
8.1 监听 fd 可读:触发 Socket::onAccept()
- 某个 poller 的 epoll_wait 返回 listen_fd READ
- 执行
Socket::onAccept(listenSockFD, event)
8.2 accept() 拿到客户端 fd
fd = accept(listen_fd, ...)- 配置 fd(nonblock、nodelay、buf等)
8.3 创建 peer Socket(优先负载均衡)
- 优先走
_on_before_accept(_poller):
- 实际执行
TcpServer::onBeforeAcceptConnection(...) return createSocket(EventPollerPool::Instance().getPoller(false));getPoller(false):按负载挑最空闲 poller(不优先当前线程)
- 如果失败才保底:
peer_sock = Socket::createSocket(_poller, false)(继承父 poller,无负载均衡)
8.4 peer_sock 绑定 fd + 回调顺序保证
-
peer_sock_fd = peer_sock->setPeerSock(fd)(同时拿到对端 ip/port) -
构造
completed(析构时会执行peer_sock->attachEvent(peer_sock_fd)) -
调用
_on_accept(peer_sock, completed)- 注意:先 onAccept 再 attachEvent,避免没设 onRead 就收数据
9) TcpServer 的 _on_accept:把连接派发到 peer_sock 所属 poller 线程
_on_accept(在TcpServer::setupEvent()注册):
auto ptr = peer_sock->getPoller().get()auto server = strong_self->getServer(ptr)(找到对应 poller 的 clone server)ptr->async([server]{ server->onAcceptConnection(peer_sock); })
10) onAcceptConnection:创建 Session + 注册 onRead/onErr
helper = _session_alloc(server, peer_sock):
- 创建
RtspSession(peer_sock) - session 继承
SocketHelper,因此继承/设置_on_create_socket - 创建
SessionHelper(server, session),加入全局SessionMap
server->_session_map.emplace(helper.get(), helper)(server 侧 map 管理)peer_sock->setOnRead(...):
- 回调里
session->onRecv(buf)→ RtspSession::onRecv(buf)
peer_sock->setOnErr(...):
- 出错时调用
session->onError(err)并从_session_map移除
11) attachEvent:把 peer fd 注册到 poller epoll,开始收发数据
- 当
_on_accept返回后,completed析构触发:
peer_sock->attachEvent(peer_sock_fd)
attachEvent:
_read_buffer = _poller->getSharedBuffer()_poller->addEvent(peer_fd, READ|WRITE|ERROR, ...)
- 之后 peer_fd READ:
Socket::onRead()->recvfrom()->_on_read(buffer, ...)
_on_read就是你在 onAcceptConnection 里 setOnRead 注册的回调:
- 最终进入
RtspSession::onRecv()
你要记住的“一句话版本”
启动阶段: main -> TcpServer -> EventPollerPool(N个poller线程) -> start 注册 session 工厂 -> setupEvent 创建监听socket并挂回调 -> listen 加入 epoll -> clone 到每个 poller
连接阶段: epoll触发 listen_fd -> accept -> onBeforeAccept 按负载选 poller 创建 peer_sock -> onAccept 把连接派发到 peer poller -> onAcceptConnection 创建 RtspSession 并 setOnRead -> attachEvent 注册 peer_fd -> onRead -> RtspSession::onRecv