04-Session
Session——会话管理有什么
Session 回答的问题是:「每个连接自己的状态放哪?」 它被逼出来的原因是「每个连接的状态散落在 EventLoop 回调里」。这一篇讲它有什么——职责、内部结构、边界、异常、形态,重点是「协议状态属于 Session,不属于 Protocol」。
一、它解决什么问题每个客户端连接都需要维护自己的状态:
123缓冲区 已收到但还没处理完的字节协议状态 握手 / 正常 / 关闭活跃时间 多久没动了(心跳判断)
在没有 Session 的阶段,这些状态散落在 EventLoop 回调里——靠全局数组、靠 fd 索引到处查。连接一多,状态管理就乱。
Session 被逼出来的原因:把「每个连接的状态」集中到一个结构里,从事件分发中抽离。
二、它有什么(内部结构)1234567struct Session { int fd; // 连接 fd Buffer recv_buf; // 接收缓冲区 Buffer send_buf; ...
03-Connection
Connection——连接管理有什么
Connection 回答的问题是:「一条连接的生死谁来管?」 它被逼出来的原因是「连接的生命周期散落在 EventLoop 的各个回调里」。这一篇讲它有什么——职责、内部结构、边界、异常、形态,以及它和 Session 的分工。
一、它解决什么问题连接不是「accept 一下就完了」。一条连接从建立到关闭,有一整套生命周期:
123建立 accept 后登记维护 收发数据、跟踪状态(正常/半关闭/超时)关闭 正常关闭 / 异常断开,清理资源
在只有 EventLoop 的阶段,这套动作散落在各个回调里:
123// on_accept 回调里:创建连接// on_readable 回调里:收数据 + 判断是否断开// on_close 回调里:清理连接
连接状态一复杂(半关闭、超时、被重置),每个回调都要重复判断「这个连接现在什么状态」。这就是 Connection 被逼出来的原因——把「一条连接的生死」独立成一个组件。
二、它有什么(内部结构)1234567891011121314class Connection ...
02-EventLoop
EventLoop——事件循环有什么
EventLoop 是网络系统的心脏。它回答的问题只有一个:「什么时候有事发生?」 它是网络系统里第一个被逼出来的组件,也是唯一拥有自己线程的组件。这一篇讲它有什么——职责、内部结构、边界、异常、形态。
一、它解决什么问题早期代码靠遍历所有连接来发现「谁有数据」:
123for (int i = 0; i < count; i++) { if (有数据可读(clients[i])) { ... }}
100 个连接里可能只有 2 个有数据,却要逐个检查。遍历太慢——这就是 EventLoop 被逼出来的原因。
解法:让操作系统直接告诉「谁有事」,不用自己查。
1int n = epoll_wait(epfd, events, MAX_EVENTS, -1); // 只返回有事的
EventLoop 的本质:把「遍历所有连接」变成「只处理有事的连接」。
二、它有什么(内部结构)EventLoop 是「数据 + 算法 + 接口」的完整组件:
123456789101112131415161 ...
01-拆分演化
网络系统是怎么被拆成这几个组件的
主线《从函数到网络系统》第七步「组件化」只甩出一句话「网络系统拆成几个组件」。这一篇把这一步展开:网络系统从一坨职责混杂的代码,是怎么一步步被拆出 EventLoop、Connection、Session、Buffer、Protocol 的。每个组件都不是「设计出来的」,而是「扛不住什么」才被逼出来的。
一、起点:一坨混在一起的代码最早的网络代码,所有职责混在一个 while 里:
12345678910111213int clients[100];int count = 0;while (1) { clients[count++] = accept(server, ...); // 管连接 for (int i = 0; i < count; i++) { if (有数据可读(clients[i])) { recv(clients[i], buf, sizeof(buf), 0); // 管收发 处理(buf); ...
05-UDP服务器与可靠性
UDP 服务器与可靠性——无连接传输下如何自建可靠
网络系统主线(01)走的是 TCP:连接建立 → 连续字节流 → Connection 管理。但很多实时场景(游戏、语音、嵌入式)用的是 UDP——无连接、以 Datagram(数据报)为单位。这一条线回答:① TCP 与 UDP 最核心的区别(生命周期与运行流两视角);② UDP 服务器怎么自建(socket → recvfrom → decode → dispatch → service → sendto);③ UDP 不代表没有客户端状态——Connection State / Session / Token 三分(详见 04-系统角色/09);④ 可靠性必须自己构建(Sequence / ACK / 重传)。
一、TCP 与 UDP 最核心的区别12TCP:连接建立(客户端→服务器→Connection)→ 连续字节流(可靠、有序)UDP:无连接,天然以 Datagram(数据报)为单位
Qt 中:TCP → QTcpServer + QTcpSocket,UDP → Q ...
04-网络系统设计从想法到实现
网络系统设计——从想法到实现
业务设计线走「需求→业务流程→功能点→功能流程→算法→数据结构」(做什么),CLI/GUI 设计线走「入口控制面→解析→分发→调用」(怎么被外部使用)。网络系统是第三种角色,它的设计线有自己的控制面——协议。本篇把这条线从想法(通信需求)一路走到实现(系统组装),每一步给出原子步骤清单。
这是 04-系统角色/04-通信系统 里设计路线的完整展开,也是 06-设计/05-通信程序的设计顺序 的角色视角版——那边讲「按什么顺序做」,本篇讲「每一步具体做什么」。
主线:七步从外到内12345678910111213① 通信需求 要解决什么通信问题 ↓② 协议设计 双方按什么规则交换什么(对端之间) ↓③ 组件设计 拆成哪些组件(内部怎么分) ↓④ 组件接口设计 每个组件暴露什么接口(组件之间) ↓⑤ 组件实现 按接口写实现 ↓⑥ 框架化 组件封装成可运行的系统 ↓⑦ 系统组装 网络系统 + 入口 + 业务 → 完整软件
每一环都是「先设计对 ...
03-组装调用
网络系统的封装与调用——从散装组件到可调用系统
组件拆出来了,但还不能被直接调用——它们只是散装零件。要先封装成可调用的系统,再决定怎么被调用。封装有两种形态:框架化(有状态,入口启动) 和 接口化(无状态,上层调用接口)。如果系统只有组件、没有封装,就要先封装成框架或接口。这一篇讲封装与调用。注意:编解码(解析)和消息路由属于入口系统,不在网络系统内部封装。
一、内部封装结构:EventLoop 是心脏封装的第一步,是把散装组件按依赖关系拼起来。起点是 EventLoop——因为它是唯一拥有自己线程的有状态组件:
1234EventLoop 是心脏: → 它在自己的线程里运行 → 它决定什么时候处理什么事件 → 其他组件被它驱动
内部的封装关系:
12345EventLoop(有状态,心脏) ├── 驱动 Connection(连接事件 → 创建/销毁连接) ├── 驱动 Session(连接建立 → 创建会话;断开 → 销毁会话) ├── 引用 Protocol(消息格式定义) └── 操作 Buffer(数据可读 → 写入缓冲;数据可写 → 从缓冲发送)
E ...
02-组件拆分
网络系统的构成——从一坨代码到清晰的边界
通信程序拆成入口系统、网络系统、业务系统三个系统。这一篇单独拆「网络系统」——它内部到底有哪些东西,各负责什么,边界怎么划。先明确两点:① 网络系统是分组件、不是分层,解析层和分发层属于入口系统;② 网络系统单独不构成软件,最多做成网络库,要配合入口系统和业务系统才能形成完整的网络通信软件。
一、从一个 socket 调用说起最简单的网络程序:
12345int sock = socket(AF_INET, SOCK_STREAM, 0);connect(sock, (struct sockaddr*)&addr, sizeof(addr));send(sock, "hello", 5, 0);recv(sock, buf, sizeof(buf), 0);close(sock);
所有东西都在一个函数里——连接、发送、接收、关闭。没有边界。这和「所有东西写在 main 里」是同一个阶段。
当程序需要同时处理多个连接时,这坨代码就不够用了:
123456789// 伪代码:同时处理 100 个连接while ( ...
01-从socket到网络系统
从 socket 到网络系统——网络通信是如何一步步长出来的
和「从指令到系统」同一种写法:从最简单的网络程序开始,每一步都是上一步「扛不住了」逼出来的。最终长出 EventLoop、协议、会话——这些概念不是某天被发明的,而是从一条 socket 调用演化出来的。
一、一条 socket 调用最开始,网络就是收发字节:
12345int sock = socket(AF_INET, SOCK_STREAM, 0);connect(sock, (struct sockaddr*)&addr, sizeof(addr));send(sock, "hello", 5, 0);recv(sock, buf, sizeof(buf), 0);close(sock);
所有东西在一个函数里——创建、连接、发送、接收、关闭。没有组件,没有分层,没有协议。和「所有东西写在 main 里」是同一个阶段。
这个阶段有:socket、地址、字节流。没有连接管理,没有协议,没有并发。
二、第二个拐弯:循环处理一条 socket 调用只能处理一次请求。要处理多次,需要循环: ...
03-游戏开发的最小闭环
游戏开发的最小闭环:空窗口 → 方块 → 移动 → 碰撞 → 关卡
游戏开发是「最小闭环」最典型的体现:空窗口 → 画一个方块 → 方块移动 → 加入碰撞 → 角色 → 敌人 → 关卡,每一步都是「已经能运行、能验证、再继续增加能力」的版本,而不是堆完代码才第一次运行。先画窗口(验证渲染闭环),再让方块动(验证输入闭环),再碰撞(验证物理闭环)——每次都是增量更新的可验证闭环。
一、游戏开发最容易犯的错:一直做外围,没有可玩闭环12345678910❌ 错误流程: 先做美术资源(画了几百张图) → 再写全部系统(渲染/物理/AI/关卡全上) → 最后组装 → 跑起来不知道好不好玩为什么错: 每一步都没验证——资源对不对不知道、系统对不对不知道 核心乐趣没验证——做完才发现不好玩 没有可玩闭环,做着做着没动力
123456✅ 正确流程: 先做一个能跑的窗口(验证渲染闭环) → 画一个会动的方块(验证输入闭环) → 加入目标/碰撞(验证规则闭环) → 能玩的一个小循环(验证核心乐趣) → 一圈圈扩大(加敌人、加关卡、加内容)
游戏开发的第一要务:先造出最小的 ...
