揭开高性能网络库中Buffer与TcpConnection设计的秘密
·
Buffer 设计核心思想
应用层非阻塞 socket 收发缓冲区,解决非阻塞 IO 下的数据粘包、半包问题。通过动态内存管理和读写指针索引实现高效数据存取。
核心成员变量与常量
std::vector<char> _buffer:动态内存容器,自动管理缓冲区内存size_t _reader_idx:读位置索引,标记已读取数据的起始位置size_t _writer_idx:写位置索引,标记已写入数据的结束位置const static int kInitialSize = 1024:默认缓冲区初始大小const static int kCheapPrepend = 8:头部预留空间大小,用于协议头等场景
主要接口功能解析
数据读取接口
const char *peek() const
返回当前可读数据的起始指针,不移动读索引
size_t readableBytes() const
返回当前可读数据的总字节数,计算公式为:
_writer_idx - _reader_idx
std::string retrieveAsString(size_t len)
提取指定长度的数据并转为字符串,同时移动读索引
数据写入接口
char *beginWrite()
返回当前可写区域的起始指针
void append(const void *data, size_t len)
追加数据到缓冲区末尾,自动处理扩容
void ensureWritableBytes(size_t len)
确保缓冲区有足够可写空间,不足时触发扩容
特殊功能接口
ssize_t readFd(int fd, int *savedErrno)
从文件描述符读取数据到缓冲区,处理 EAGAIN 等非阻塞场景
std::optional<std::string> getline()
按行提取数据,使用 C++17 optional 安全返回
内存管理策略
void makeSpace(size_t len)
核心扩容算法:
- 检查当前空闲空间(前置空间 + 尾部空间)是否足够
- 如果足够,移动数据到缓冲区头部
- 如果不足,触发 vector 扩容
移动数据条件:
(prependableBytes() + writableBytes()) < (len + kCheapPrepend)
性能优化点
- 减少内存拷贝:通过移动数据而非重新分配
- 预留空间:头部预留 8 字节避免频繁移动
- 批量操作:支持大块数据一次性处理
- 零拷贝接口:直接暴露指针给上层
典型使用场景
Buffer buf;
// 读取数据
ssize_t n = buf.readFd(sockfd, &errno);
if(n > 0) {
// 处理一行数据
auto line = buf.getline();
if(line) {
process(*line);
}
}
// 发送数据
buf.append("response", 8);
send(buf.peek(), buf.readableBytes());
更多推荐



所有评论(0)