libnids源码分析(libuv源码分析)

libnids源码分析(libuv源码分析)p id main toc strong 目录 strong p 引言 Buffer 类的成员数据 Buffer 类的构造 一些下标获取函数 Buffer 类的扩容策略 清空缓冲区 读取和写入数据 和预留空间相关的接口 读取一行 本文适合于了解多路转接

大家好,我是讯享网,很高兴认识大家。



 <p id="main-toc"><strong>目录</strong></p> 

讯享网

引言

Buffer类的成员数据

Buffer类的构造

一些下标获取函数

Buffer类的扩容策略

清空缓冲区

读取和写入数据

和预留空间相关的接口

读取一行



讯享网

         本文适合于了解多路转接,了解muduo库的读者或想要看muduo库源码的读者,本文会把Buffer类的核心源码拎出来解读。

        个人主页:东洛的克莱斯韦克-CSDN博客

        另一篇muduo源码解析的文章:muduo库EventLoop类源码解析-CSDN博客

        我之前写过一篇文章【C++】设计用户级缓冲区_c++ 缓冲区设计-CSDN博客,其思想就是来源于muduo库Buffer类的源码。但代码的细节个人不是很满意,这篇文章带你探秘muduo源码是怎么实现用户级缓冲区。

        为什么要有用户级缓冲区?

        在网络通信中TCP的缓冲区是内核缓冲区,要拿到数据,在用户和内核之间始终有一次拷贝,这样似乎就有理由需要一层用户缓冲区来保存数据。但这样的理由似乎是不充分的,因为缓冲区不是处理数据的地方,如果要进行数据处理还需要一次拷贝,似乎,有些浪费性能了?

        但从拷贝数据的角度说确实如此,对于网络通信来说,在TCP缓冲区中可能不足一条完整的数据,这时是不是需要保存一下数据呢?但这样的理由依旧是不充分的,因为数据的完整性可以有更上层的协议来保证,比如HTTP协议,网络库没必要在数据完整性方面做太多事情。

        muduo库是Reactor模型,应该从事件监控和事件分发的角度理解缓冲区的意义。对于IO线程来说,触发了读事件,说明TCP读缓冲区中有数据了,触发写事件,说明TCP写缓冲区中有剩余空间了。对IO线程来说,事件的触发是异步的,因为IO线程不可能知道事件什么时候触发,那么IO线程什么时候向TCP缓冲区里读或写呢?IO线程自己肯定不知道,他需要事件通知才能知道。

        所以,IO线程读数据和写数据的对对象就不能是TCP缓冲区了,不然很大概率是操作失败或者阻塞住,这在性能上的损耗是不小的。当IO线程有了一层用户级缓冲区后,读和写的操作对象是用户级缓冲区,大概率是成功的,几乎不会阻塞。而和TCP缓冲区交互的对象就成了用户级缓冲区。

讯享网

用类型std::vector<char>来存数据,vector类型是线性空间,char类型是一字节,很符合TCP的字节流的概念。

readerIndex_是读下标,writerIndex_写下边,者两个下标是左闭右开的,readerIndex_是可读数据的第一个字节的数据的下标,writerIndex_是可读数据最后一个字节数据的后一个下标。

const char Buffer::kCRLF[] = " "; kCRLF为了方便读取一行数据。

空间划分,用两个下标(分别是读下标和写下标)把空间分为三段:

        第一段:0下标到读下标(左闭右开),这段为剩余空间

        第二段:读下标到写下标(左闭右开),这段为可读数据

        第三段:写下标到空间末尾(左闭右开),这段为剩余空间

muduo库的缓冲区有一个很不错的设计——为空间预留了8字节的数据。这8字节数据一般是用来说明接下来需要接收多少数据才算一条完整的数据。当然,也可以不用。缓冲区的空间大小被初始化为了1032字节

 
讯享网

        由私有成员函数makeSpace来保证缓冲区的空间足够。如果要扩容的空间大小大于剩余空间(第一段加第三段空间大小)就不考虑第一段空间大小,直接把第三段空间大小扩到足够大。

 

        在判断的时候,muduo库还考虑了预留空间(8字节大小)。这里要说明的是muduo空用vector作为容器扩容的时候要用resize(),因为我们把vector的有效数据大小作为了缓冲区的空间大小,而vector容器实际的空间大小可能更大,所以,在用resize()给缓冲区扩容的时候,不一定会发生异地扩容。

        那如果剩余空间(第一段加第三段空间大小)小于等于要扩容的大小呢?把缓冲区上的有效数据移动大缓冲区的开始,这样就腾出来的空间就可以放新的数据了。

讯享网

        清空缓冲区就是下标的操作,对于缓冲区上的数据来说,允许被覆盖就说明该数据被清空了,在缓冲区中只有第二段的空间才是不可被覆盖的。

 

源码的逻辑也很简单,如果要清空的数据大小小于可读数据大小,就挪动可读下标,不然全部清空。

讯享网

        读取很简单,构造一个临时的std::string向返回给上层。

 

下面是向缓冲区写入数据

讯享网

        前文提到过,muduo库的缓冲区会在空间的起始位置预留8字节空间。在网络通信中,我们一般用这8字节空间表述一段完整数据的大小。

        其中有一个的功能是在可读数据的前一段空间填充数据。实现如下

 

        如果我们在这个成员函数的基础上封装一层呢

讯享网

        这个函数的功能就是在可读数据的前面填充8字节大小的数据。对应的,我们还可以读取8字节的数据。如下实现

 

上述函数的返回值是int64_t,其实你可你把他强转成其他类型,比如char。

        对于网络通信来说,那么会有读取一行数据的需求。这里只列举一个函数,它的实现就是在缓冲区的可读数据里找“ ”字符

讯享网

小讯
上一篇 2025-06-11 11:43
下一篇 2025-05-12 18:53

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/172732.html