02-组件拆分
网络系统的构成——从一坨代码到清晰的边界
通信程序拆成入口系统、网络系统、业务系统三个系统。这一篇单独拆「网络系统」——它内部到底有哪些东西,各负责什么,边界怎么划。先明确两点:① 网络系统是分组件、不是分层,解析层和分发层属于入口系统;② 网络系统单独不构成软件,最多做成网络库,要配合入口系统和业务系统才能形成完整的网络通信软件。
一、从一个 socket 调用说起
最简单的网络程序:
1 | int sock = socket(AF_INET, SOCK_STREAM, 0); |
所有东西都在一个函数里——连接、发送、接收、关闭。没有边界。这和「所有东西写在 main 里」是同一个阶段。
当程序需要同时处理多个连接时,这坨代码就不够用了:
1 | // 伪代码:同时处理 100 个连接 |
连接管理、数据收发、业务处理全混在一起。 这就是「需要拆分」的信号。
二、网络系统里的东西分三类,不是五个组件
网络系统里不是「五个组件」,而是三类性质不同的东西:
1 | 网络系统 |
为什么这样分?关键看「组件」的定义:组件 = 数据结构 + 算法 + 接口,它有行为、能被调用。
1 | 有状态组件(EventLoop / Connection / Session): |
注意这里的边界:
1 | ✅ 网络系统只关心:字节怎么收发、连接怎么管理、会话怎么维护、协议格式是什么 |
解析(parse)和调度(dispatch)不属于网络系统——它们是入口系统(通信程序)的组件。网络系统处理的是「字节」,入口系统处理的是「消息」。
三、协议层 vs 解析层:协议是数据结构,解析是转换
这是最容易混的地方,必须分清楚:
1 | 协议(Protocol,属于网络系统): |
1 | protocol.cpp(协议定义,网络系统): |
两者是不同的东西:
1 | 协议回答「消息长什么样」(定义,一份数据结构) |
那「协议状态」(握手/心跳/关闭)在哪? 它不属于 Protocol——那是 Session 的状态。Session 里有一个 state 字段记录「当前在握手、还是正常、还是关闭中」。协议格式是死的定义,协议状态是活的、属于某个会话的。
四、三类东西的边界与异常
EventLoop(有状态组件,有生命周期)
问题:100 个连接同时有数据,程序怎么知道该处理哪个?
1 | EventLoop:调用 epoll_wait/kqueue 等待事件 |
边界:只管「什么时候有事发生」,不管事情是什么。
异常:事件循环被中断、fd 耗尽、定时器精度不够。
Connection(有状态组件)
问题:一条连接从建立到关闭,生命周期谁来管?
1 | 连接建立:accept 后登记 |
边界:只管「一条连接的生死」,不管字节是什么意思。
异常:连接被拒绝、被重置、超时断开。
Session(有状态组件)
问题:每个客户端连接需要维护自己的状态,状态放哪?
1 | struct Session { |
边界:EventLoop 只管事件分发,Session 管状态。
异常:会话过期、协议版本不匹配(握手失败)、协议状态机异常。
Buffer(无状态工具)
问题:收发的数据先存哪?
1 | 接收缓冲:字节先存进来,攒够一条消息再处理 |
Buffer 是无状态工具——有数据(字节数组)+ 算法(读写指针、扩容)+ 接口(read/write),但它不持有跨调用的状态、不需要初始化,被读写时才操作。
边界:只管「数据暂存」,不管内容。
异常:缓冲区满。
Protocol(数据结构,不是组件)
问题:收发的字节长什么样?
1 | 协议格式定义:消息格式(头+体)、字段含义、版本 |
边界:只是「消息长什么样」的定义(struct Message),不做字节↔消息的转换(那是入口系统解析层的事)。
异常:协议本身没有异常——它是死的定义。版本不匹配是 Session 的异常,格式错误是解析层的异常。
五、组件之间的数据流
1 | 字节从网卡进来 |
网络系统只负责字节进、字节出;中间的消息解析和分发是入口系统的事。
六、有状态 vs 无状态:封装形态由性质决定
这些东西怎么封装成可调用的形态?由它们的性质决定:
1 | 有状态组件(有状态、有生命周期)→ 类封装: |
一个术语澄清:这里的「类封装 / 函数导出」就是接口形态演化线里说的「框架化 / 接口化」——它对应的不是「谁创建组件」,而是两种使用方式:
1 | 接口形态(本线,11-模块与构建单元/01):框架化 = 有状态 → 类封装;接口化 = 无状态 → 函数导出 |
系统封装成哪种形态,就决定它怎么被使用——框架化由入口启动,接口化由上层调用。下一篇展开。
七、每个组件的异常处理差异
每个组件遇到的异常完全不同,处理方式也完全不同:
1 | EventLoop 异常: |
消息格式错误是解析层(入口系统)的异常,不属于网络系统。 协议本身(数据结构)没有异常。
每个组件的异常是独立的——EventLoop 不应该处理连接断开(那是 Connection 的事),Session 不应该处理缓冲区满(那是 Buffer 的事),解析失败(那是入口系统解析层的事)。
八、网络系统 vs 入口系统的边界总结
1 | 网络系统(3 有状态组件 + 1 无状态工具 + 1 数据结构): |
三个系统各管一段:网络系统管字节,入口系统管消息,业务系统管业务。
网络系统单独只是库
网络系统不是软件,它只是完整软件里的一个系统:
1 | 只有网络系统 → 最多做成网络库(如 libevent / libuv / asio),提供网络能力 |
一个网络库可以独立存在、被任何程序链接;但要「形成软件」,必须有入口系统把网络事件解析成消息、路由给业务 Handler,有业务系统真正处理业务。
收束
1 | 网络系统里分三类东西,不是五个组件: |
网络系统是分组件、不是分层;协议是数据结构、不是组件;网络系统单独是库、不是软件。 抓住这三条,就不会再把 Protocol 当组件、把「框架化/接口化」的接口形态和组装方式混在一起。
本篇讲网络系统「内部有哪些东西」(构成视角);
04-网络系统设计从想法到实现讲「拿到通信需求后按什么步骤设计」(设计视角,含组件设计的原子步骤清单)。
