Buffer——缓冲区有什么
Buffer 回答的问题是:「收发的字节先存哪?」 它被逼出来的原因是「TCP 是流式协议,一次收不到整条消息」。这一篇讲它有什么——职责、内部结构、边界、异常、形态。注意:Buffer 是无状态工具,不是有状态组件。
一、它解决什么问题
TCP 是流式协议,不是消息协议:
1 2 3 4
| 一次 recv 可能收到: → 半条消息(消息还没发完) → 一条半消息(两条粘在一起) → 恰好一条(运气好)
|
调用者不能假设「一次 recv = 一条消息」。需要一个地方先把字节攒起来,攒够一条消息再处理。这就是 Buffer 被逼出来的原因。
1 2
| 接收:字节先存进 Buffer,攒够一条消息再取出处理 发送:待发的数据先排进 Buffer,再逐步写出
|
二、它有什么(内部结构)
Buffer 是「数据 + 算法 + 接口」,但无状态:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Buffer { std::vector<char> data_; size_t read_index_; size_t write_index_;
public: void append(const char* p, size_t n); size_t readable() const; void retrieve(size_t n); const char* peek() const; };
|
1 2 3
| 数据:字节数组 + 读指针 + 写指针 算法:写入(append)、读出(peek/retrieve)、扩容 接口:append / readable / peek / retrieve
|
核心操作:
1 2 3
| append(bytes) ← 从网络收进来的字节,先 append 进 Buffer peek() ← 看「现在攒了多少、够不够一条消息」 retrieve(n) ← 攒够一条消息,取出处理,指针前移
|
三、它的边界:只管「暂存」,不管「内容」
1 2 3
| ✅ 管:字节的暂存、读写指针、扩容 ❌ 不管:字节是什么消息(解析层的事) ❌ 不管:连接状态(Connection/Session 的事)
|
1 2 3
| Buffer 存了一堆字节 → 它不知道这是「登录消息」还是「心跳」 → 它只知道「这里有 N 个字节可读」
|
Buffer 是纯粹的「字节容器」——存进去、读出来,仅此而已。
四、它的形态:无状态工具,函数导出
Buffer 和有状态组件(EventLoop/Connection/Session)本质不同:
1 2 3 4 5
| 无状态:Buffer 不持有「跨调用的连接状态」 → 它的状态(字节数组、指针)就是它的数据本身 → 不需要初始化,不管理生命周期
被调用才操作:被 append / peek / retrieve 时才动
|
所以它是无状态工具——「数据结构 + 算法 + 接口」,但没有生命周期、不需要类封装的那套「构造/析构/start/stop」。通常一个 Buffer 直接内嵌在 Session 里用:
1 2 3 4 5
| struct Session { Buffer recv_buf; Buffer send_buf; ... };
|
五、它的异常
1 2 3
| 缓冲区满: 写入速度超过处理速度,Buffer 塞满 → 处理:丢弃数据、限流、记录日志、或断开连接
|
Buffer 的异常很少,因为它的职责很窄——它就是「暂存」,唯一的异常就是「存不下了」。
六、它和其他组件的关系
1 2 3
| Buffer ← 内嵌在 Session 里(recv_buf / send_buf) Buffer ← 被 EventLoop 间接操作(可读 → append;可写 → retrieve 发送) Buffer 不主动依赖任何组件——它是被用的「工具」
|
1 2
| EventLoop ──可读──→ Session ──append──→ Buffer(收) EventLoop ──可写──→ Session ──retrieve→ Buffer(发)
|
Buffer 是网络系统里最「被动」的东西——没有自己的控制流,没有生命周期,谁用谁操作。
七、小结
1 2 3 4 5 6 7 8 9
| Buffer 有什么: 职责:字节暂存(收进来、发出去) 结构:字节数组 + 读指针 + 写指针 接口:append / peek / retrieve / readable 边界:只管「暂存」,不管「内容」 形态:无状态工具,函数导出,内嵌在 Session 里 异常:缓冲区满
一句话:Buffer 是「字节的暂存箱」,被读写时才动,没有自己的生命周期。
|