1️⃣ 概述

在高性能网络框架中,TcpServer 模块往往处于 “承上启下” 的核心地位:

  • 向下 封装了底层的 Socket 套接字细节,
  • 向上 为各类 应用层协议 提供基础的连接管理和数据传输能力。

sylar 框架中,TcpServer 是一个高层次的封装,旨在提供一个 简单稳定支持多核并发TCP 服务器基类。
TcpServer 深度集成了 协程调度器 (IOManager) 和 套接字封装 (Socket),使得开发者可以专注于业务逻辑,而无需处理复杂的非阻塞 I/O 和多线程竞争。

2️⃣ 设计理念

sylar 框架中,TcpServer 不仅仅是一个简单的套接字监听器,它是底层网络组件与复杂业务协议之间的核心桥梁。其架构设计的核心灵魂在于:通过高度解耦实现极致的并发效率与扩展性

基于 Reactor 模式的双调度器架构

这是 TcpServer 高并发能力的核心。它将网络事件处理划分为两个阶段,并允许通过不同的 IOManager 进行物理隔离:

  • Accept 调度器: 专注于监听(listen)与接收(accept)。只负责捕获新连接,确保即使在高负载下,新用户的接入响应依然迅速。
  • IO 调度器: 专注于具体的业务逻辑(Handle)。一旦连接建立,任务会被立即 “派发”IO 调度器,从而避免了耗时的业务计算阻塞后续连接的接入。

对象生命周期的异步安全

协程异步 编程中,最棘手的问题莫过于 “对象在任务执行中途被析构”

  • shared_from_this 的妙用:
    • TcpServer 继承自 std::enable_shared_from_this
    • start()startAccept() 中,通过传递 shared_from_this() 给协程调度器,确保了只要还有连接在处理,TcpServer 对象就始终存活,彻底杜绝了悬空指针引发的崩溃。

协议无关性与高扩展性

TcpServer 本身并不关心传输的是 HTTP 网页还是 Redis 指令:

  • 业务逻辑抽象化: 通过将 handleClient 设计为 Virtual Function,基类只负责管理连接的接入和 Socket 的生命周期,而具体的 “协议解析”“响应逻辑” 完全交给子类去实现。

优雅停机

利用 m_isStop 标志位结合 stop() 方法,系统能够有序地关闭所有监听中的 Socket,并取消cancel 调度器上的残留任务,保证了服务在退出时不会产生僵尸连接或资源泄漏。

3️⃣ 启动流程

构造函数

1
2
3
4
5
6
7
8
TcpServer::TcpServer(IOManager* ioworker, IOManager* accept)
: m_ioworker(ioworker)
, m_acceptworker(accept)
, m_recvtimeout(g_tcp_server_read_timeout->getValue())
, m_name("syalr/1.0.0")
, m_type("tcp")
, m_isStop(true)
{}
  • m_acceptworke:负责 接收客户连接 的线程调度器。
  • m_ioworker:负责后续 处理客户读写连接 handleClient 的线程调度器。
  • m_recvtimeout:处理客户端连接超时
  • m_name:服务器的名称
  • m_type:服务器的类型
  • m_isStop:是否处于停止状态

绑定监听阶段 bind

由于服务器可能有多个 ip:port,因此分为 单地址版本多地址版本
为每个 ip:port 建立 socket,对其进行监听。如果有一个地址 绑定监听 失败,就认为 bind 失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
bool TcpServer::bind(Address::ptr address)
{
std::vector<Address::ptr> addrs;
std::vector<Address::ptr> fails;
addrs.push_back(address);
return bind(addrs, fails);
}

bool TcpServer::bind(std::vector<Address::ptr>& addrs, std::vector<Address::ptr>& fails)
{
for(auto& addr : addrs)
{
Socket::ptr sock = Socket::CreateTCP(addr);
if (!sock->bind(addr))
{
SYLAR_LOG_ERROR(g_logger) << "bind fail errno="
<< errno << " errstr=" << strerror(errno)
<< " addr=[" << addr->toString() << "]";
fails.push_back(addr);
continue;
}
if (!sock->listen())
{
SYLAR_LOG_ERROR(g_logger) << "listen fail errno="
<< errno << " errstr=" << strerror(errno)
<< " addr=[" << addr->toString() << "]";
fails.push_back(addr);
continue;
}
m_socks.push_back(sock);
}

if(!fails.empty())
{
m_socks.clear();
return false;
}

for(auto& i : m_socks)
{
SYLAR_LOG_INFO(g_logger) << "type=" << m_type
<< " name=" << m_name
<< " server bind success: " << *i;
}
return true;

}

启动阶段 start/ startAccept

针对每个 sock,先将其扔进 accpet 任务队列,等待 accept 中的协程执行函数 startAccept 启动对 sock 的监听,如果客户连接,在工作线程 io_worker 中投递任务 handleClient ,等待空闲协程处理对该用户的连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool TcpServer::start()
{
if (!m_isStop)
{
return true;
}

m_isStop = false;
for(auto& sock : m_socks)
{
m_acceptworker->schedule(std::bind(&TcpServer::startAccept, shared_from_this(), sock));
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void TcpServer::startAccept(Socket::ptr sock)
{
while (!m_isStop)
{
Socket::ptr client = sock->accept();
if (client)
{
m_ioworker->schedule(std::bind(&TcpServer::handleClient, shared_from_this(), client));
}
else
{
SYLAR_LOG_ERROR(g_logger) << "accept errno=" << errno
<< " errstr=" << strerror(errno);
}
}

}

执行阶段 handleClient

handleClient 被定义为 virtual 虚函数。因为:

  • 基类 TcpServer 只负责底层的网络基础设施(如 bindlistenaccept)。
  • 具体的应用协议(如 HTTP 解析、Echo 回显等)全部通过在子类中重写 handleClient 来实现。
    1
    2
    3
    4
    void TcpServer::handleClient(Socket::ptr client)
    {
    SYLAR_LOG_INFO(g_logger) << "handleClient: " << *client;
    }

停止阶段 stop

设置停止标志 m_isStop = true,这会让所有 startAccept 循环在下一轮检查时退出。在 accept 线程中投递任务:对所有sock 停止关注,并关闭 sock,以及清理 sock 集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
void TcpServer::stop()
{
m_isStop = true;
auto it = shared_from_this();
m_acceptworker->schedule([this, it](){
for(auto& sock : m_socks)
{
sock->cancelAll();
sock->close();
}
m_socks.clear();
});
}