C 仿 muduo 高并发服务器用户态缓冲区 Buffer 模块实现1. 为什么需要 Buffer在非阻塞 I/O 的网络编程中read和write不保证一次就能读完或写完所有数据读半包一次read可能只收到请求的一部分需要暂存起来等下次数据到达再拼接。写半包一次write可能只发送了部分数据剩余数据需要缓冲等 socket 可写时继续发送。粘包多次发送的数据可能被合并成一个 TCP 包到达需要按协议格式拆分。因此服务器需要一个用户态缓冲区在应用层管理数据的接收和发送。muduo 中的Buffer就是这样一个组件本项目仿照它实现了一个简化版。2. 代码总览#include vector #include stdint.h #include assert.h #include iostream #include string #include cstring #include ctime #define BUFFER_DEFAULT_SIZE 1024 class Buffer { private: std::vectorchar _buffer; // 使用 vector 管理内存 uint64_t _reader_idx; // 读偏移 uint64_t _writer_idx; // 写偏移 public: Buffer() : _reader_idx(0), _writer_idx(0), _buffer(BUFFER_DEFAULT_SIZE) {} char *Begin() { return *_buffer.begin(); } char *WritePosition() { return Begin() _writer_idx; } char *ReadPosition() { return Begin() _reader_idx; } uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; } uint64_t HeadIdleSize() { return _reader_idx; } uint64_t ReadAbleSize() { return _writer_idx - _reader_idx; } void MoveReadOffset(uint64_t len) { assert(len ReadAbleSize()); _reader_idx len; } void MoveWriteOffset(uint64_t len) { assert(len TailIdleSize()); _writer_idx len; } void EnsureWriteSpace(uint64_t len) { if (TailIdleSize() len) return; if (len TailIdleSize() HeadIdleSize()) { uint64_t rsz ReadAbleSize(); std::copy(ReadPosition(), ReadPosition() rsz, Begin()); _reader_idx 0; _writer_idx rsz; } else { _buffer.resize(_writer_idx len); } } void Write(const void *data, uint64_t len) { EnsureWriteSpace(len); const char *d (const char *)data; std::copy(d, d len, WritePosition()); } void WriteAndPush(const void *data, uint64_t len) { Write(data, len); MoveWriteOffset(len); } void WriteString(const std::string data) { return Write(data.c_str(), data.size()); } void WriteStringAndPush(const std::string data) { WriteString(data); MoveWriteOffset(data.size()); } void WriteBuffer(Buffer data) { return Write(data.ReadPosition(), data.ReadAbleSize()); } void WriteBufferAndPush(Buffer data) { WriteBuffer(data); MoveWriteOffset(data.ReadAbleSize()); } void Read(void *buf, uint64_t len) { assert(len ReadAbleSize()); std::copy(ReadPosition(), ReadPosition() len, (char *)buf); } void ReadAndPop(void *buf, int len) { Read(buf, len); MoveReadOffset(len); } std::string ReadAsString(uint64_t len) { assert(len ReadAbleSize()); std::string str; str.resize(len); Read(str[0], len); return str; } std::string ReadAsStringAndPop(uint64_t len) { assert(len ReadAbleSize()); std::string str ReadAsString(len); MoveReadOffset(len); return str; } char *FindCRLF() { char *res (char *)memchr(ReadPosition(), \n, ReadAbleSize()); return res; } std::string GetLine() { char *pos FindCRLF(); if (pos NULL) return ; return ReadAsString(pos - ReadPosition() 1); } std::string GetLineAndPop() { std::string str GetLine(); MoveReadOffset(str.size()); return str; } void Clear() { _reader_idx 0; _writer_idx 0; } };3. 核心设计思想Buffer 的设计围绕三个核心读写偏移分离用_reader_idx和_writer_idx标记读位置和写位置而不是每次读走数据就删除 vector 头部。这样避免频繁内存移动提高效率。空间整理与扩容当尾部空间不足时先尝试把可读数据移动到头部利用头部空闲空间如果总空间仍不足再对 vector 扩容。按行读取支持提供FindCRLF、GetLine、GetLineAndPop方便 HTTP 等文本协议按行解析。4. 成员变量与接口拆解4.1 成员变量std::vectorchar _buffer; // 底层内存 uint64_t _reader_idx; // 读偏移 uint64_t _writer_idx; // 写偏移_buffer用std::vectorchar管理内存自动扩容。_reader_idx下一个待读取数据的位置。_writer_idx下一个待写入数据的位置。可读数据区间[_reader_idx, _writer_idx)。空闲空间头部空闲[0, _reader_idx)尾部空闲[_writer_idx, _buffer.size())4.2 获取位置char *Begin() { return *_buffer.begin(); } char *WritePosition() { return Begin() _writer_idx; } char *ReadPosition() { return Begin() _reader_idx; }Begin()返回 vector 首元素地址。*_buffer.begin()是获取首地址的惯用写法。WritePosition()写起始地址。ReadPosition()读起始地址。4.3 空闲空间计算uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; } uint64_t HeadIdleSize() { return _reader_idx; } uint64_t ReadAbleSize() { return _writer_idx - _reader_idx; }TailIdleSize()写偏移之后的空闲空间。HeadIdleSize()读偏移之前的空闲空间。ReadAbleSize()可读数据大小。4.4 移动偏移void MoveReadOffset(uint64_t len) { assert(len ReadAbleSize()); _reader_idx len; } void MoveWriteOffset(uint64_t len) { assert(len TailIdleSize()); _writer_idx len; }移动前用assert检查越界Debug 下可及时发现问题。读偏移后移表示数据已被消费。写偏移后移表示数据已写入。4.5 确保可写空间整理与扩容void EnsureWriteSpace(uint64_t len) { if (TailIdleSize() len) return; if (len TailIdleSize() HeadIdleSize()) { uint64_t rsz ReadAbleSize(); std::copy(ReadPosition(), ReadPosition() rsz, Begin()); _reader_idx 0; _writer_idx rsz; } else { _buffer.resize(_writer_idx len); } }这是 Buffer 最核心的函数之一尾部空间足够直接返回。尾部不够但头部尾部够把可读数据整体移动到头部。保存可读数据大小rsz。用std::copy把数据搬到Begin()。_reader_idx 0_writer_idx rsz。这样尾部就腾出了空间。总空间不够直接扩容到_writer_idx len保证能写下新数据。不移动数据因为移动也没用总空间不够。为什么先移动再扩容移动可以复用头部空闲空间减少内存分配次数提高性能。4.6 写入数据void Write(const void *data, uint64_t len) { EnsureWriteSpace(len); const char *d (const char *)data; std::copy(d, d len, WritePosition()); } void WriteAndPush(const void *data, uint64_t len) { Write(data, len); MoveWriteOffset(len); }Write只拷贝数据不移动写偏移。WriteAndPush拷贝后移动写偏移表示数据已写入。其他写入接口都是对这两个的封装WriteString/WriteStringAndPush写入std::string。WriteBuffer/WriteBufferAndPush把另一个 Buffer 的可读数据写入当前 Buffer。4.7 读取数据void Read(void *buf, uint64_t len) { assert(len ReadAbleSize()); std::copy(ReadPosition(), ReadPosition() len, (char *)buf); } void ReadAndPop(void *buf, int len) { Read(buf, len); MoveReadOffset(len); }Read只拷贝数据不移动读偏移。ReadAndPop读取后移动读偏移表示数据已消费。ReadAsString/ReadAsStringAndPop读取为std::string。注意ReadAndPop的参数是int len而其他接口是uint64_t类型不一致建议统一为uint64_t。4.8 按行读取char *FindCRLF() { char *res (char *)memchr(ReadPosition(), \n, ReadAbleSize()); return res; } std::string GetLine() { char *pos FindCRLF(); if (pos NULL) return ; return ReadAsString(pos - ReadPosition() 1); } std::string GetLlineAndPop() { // 拼写错误应为 GetLineAndPop std::string str GetLine(); MoveReadOffset(str.size()); return str; }FindCRLF()在可读数据中查找\n返回指针。GetLine()从读位置到\n包含\n读取为字符串。如果没找到换行返回空串。GetLineAndPop()读取一行并移动读偏移。为什么找\n而不是\r\nHTTP 协议中行结束可能是\r\n或\n查找\n更通用解析时再处理\r。注意函数名拼写错误GetLlineAndPop应为GetLineAndPop。4.9 清空缓冲区void Clear() { _reader_idx 0; _writer_idx 0; }只重置偏移量不释放内存下次写入直接覆盖。5. 关键流程5.1 写入流程调用Write或WriteAndPush。EnsureWriteSpace(len)确保空间足够尾部够 → 直接写。尾部不够但总空闲够 → 数据移到头部再写。总空间不够 → 扩容再写。std::copy拷贝数据到WritePosition()。如果是WriteAndPushMoveWriteOffset(len)。5.2 读取流程调用Read或ReadAndPop。assert检查可读数据足够。std::copy从ReadPosition()拷贝到目标缓冲区。如果是ReadAndPopMoveReadOffset(len)。5.3 按行解析流程FindCRLF()用memchr找\n。GetLine()计算长度并读取。GetLineAndPop()读取后移动读偏移。如果没找到\n返回空串等待更多数据。6. 使用示例Buffer buf; std::string data GET /hello HTTP/1.1\r\nHost: localhost\r\n\r\n; buf.WriteStringAndPush(data); std::string line buf.GetLineAndPop(); // GET /hello HTTP/1.1\r\n std::cout line; while (true) { std::string l buf.GetLineAndPop(); if (l.empty()) break; std::cout l; }
企业数字化 ERP 产品动态
相关推荐
数组技术笔记 一、数组是什么
数组属于引用类型,是用来批量保存一组相同类型数据的数据结构。核心特点:数组长度一旦确定,就不可以更改。二、数组两种定义方式
方式 1:先指定长度,后面再赋值
// 格式:数据类型[] 数组名 … · 2026/9/24 17:43:06
为什么选择GEO全托管?当然是降低试错成本 很多企业认可GEO价值,但缺少懂AI大模型采信规则的团队,自行试错容易踩入AI投毒、虚夸宣传的合规陷阱,内容产出后收录、引用效果不稳定。径量GEO提供从诊断到复盘的全链路托管,坚持白帽真实合规原则,降低企业入局GEO的人… · 2026/9/24 17:42:53
驱动——arm-linux应用程序开发 交叉开发——交叉编译环境准备1.串口工具用于充当开发板终端2.网络用于传输文件(挂载) --- 确保开发板能ping通虚拟机(ubuntu)3.安装nfs并设置nfs服务目录mount -o nolock,nfsvers3 192.168.0.3:/home/linux/nfs /mnt4.在ubuntu的… · 2026/9/24 17:42:47
降AI率工具实测:从65%到12%的修改流程与避坑指南 你有没有遇到过这种情况:论文写到凌晨三点,终于用AI工具把初稿赶出来了,结果导师看了一眼就皱眉:“这语言风格一读就是AI写的,重复率倒是过了,AIGC检测估计又得飘红。”于是你开始搜各种“降AI率”工具。说… · 2026/9/24 19:18:40
PostGraphile 的 PostgreSQL JWT 规范:将 JWT Claims 序列化进数据库会话 后端API网关 【免费下载链接】crystal 🔮 Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more! 项目地址: https://gitcode.com/gh_mirrors/cry/crystal 点击查看 免费下载 导读
本文围绕 PostGrap… · 2026/9/24 19:18:40
呼和浩特朋友圈广告大促投放:服务商筛选与实操指南 1. 大促节点下呼和浩特本地朋友圈广告投放的底层逻辑1.1 为什么大促期间的朋友圈广告和平时完全是两码事做过本地投放的人都有一个共识:大促节点的朋友圈广告,和平时的日常投放,本质上不是同一个物种。平时你投朋友圈广告,拼的是素… · 2026/9/24 19:18:33
HCIE数通备考指南:官方、培训与免费资源全攻略 年年有人问HCIE数通认证考试的课程去哪里看,今年尤其多,毕竟备考节奏要往前赶。作为一个在数通这行摸爬滚打了十几年、前几年刚把HCIE证书拿下来的老工程师,我太清楚大家的问题了——不是不想学,是资源太多不知道从哪儿下手&#… · 2026/9/24 19:18:33
无人机光伏缺陷检测落地指南:从数据切图到模型训练与巡检报表 简介:针对无人机场景的光伏面板缺陷检测需求,该资源提供了一套可直接运行与二次开发的完整项目方案。项目以Python语言编写,融合OpenCV、TensorFlow、Keras等工具链,覆盖图像采集、预处理、分割、特征提取及缺陷分类全流程&#x… · 2026/9/24 19:18:33
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44