欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

muduo库分析——net篇(6)Buffer

程序员文章站 2022-06-14 10:18:01
...

参考MuduoManual

为什么要有应用层Buffer?

muduo库分析——net篇(6)Buffer


前面prependable是预留空间,有一些控制信息,比如说后面有多少字节,或者一些数据的校验码等等可以直接放在这里,而不需要重新分配空间进行内容复制,这是一种空间换时间的方法

中间readable是可读的信息,表示这里的数据没有读取取出

后面writable是可写的空间,表示这里的空间还没有数据写入

muduo库分析——net篇(6)Buffer


在TcpConnection.h中可以看见有两个Buffer成员,input是用户用于从socket中读取数据,output是用户往socket中写出数据

其他函数都是指针相关的操作或者数据的拷贝,没有什么困难的地方


这是从socket中读取所有数据,内核态的socket缓冲区大小默认接受64k和发送64k的话(可以更改),那么两个Buffer需要写成固定大小64k字节,但是一般来说这么大的空间很少用到,我们可以利用vector的resize功能,将数据慢慢变大,因为不知道socket有多少数据,readFd为了一次性读完socket中的数据(虽然这是LT模式,但为了方便起见),建立了一个临时的栈上空间char extrabuf[65536],假如vector<char>中的可用空间够大了(大于64k),那么肯定可以一次性读取完成所有的数据,否则就加上这个临时空间一起去读取socket数据。

这样做的好处是既能一次性读取完socket的数据,又能避免一开始建立的TcpConnection占用空间过大,一次性读完可以避免重复调用读取导致系统开销过大

ssize_t Buffer::readFd(int fd, int* savedErrno)
{
  // saved an ioctl()/FIONREAD call to tell how much to read
  char extrabuf[65536];
  struct iovec vec[2];
  const size_t writable = writableBytes();
  vec[0].iov_base = begin()+writerIndex_;
  vec[0].iov_len = writable;
  vec[1].iov_base = extrabuf;
  vec[1].iov_len = sizeof extrabuf;
  // when there is enough space in this buffer, don't read into extrabuf.
  // when extrabuf is used, we read 128k-1 bytes at most.
  const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;//两块一起读取或者只用buffer空间读取
  const ssize_t n = sockets::readv(fd, vec, iovcnt);
  if (n < 0)
  {
    *savedErrno = errno;
  }
  else if (implicit_cast<size_t>(n) <= writable)
  {
    writerIndex_ += n;
  }
  else
  {
    writerIndex_ = buffer_.size();
    append(extrabuf, n - writable);
  }
  // if (n == writable + sizeof extrabuf)
  // {
  //   goto line_30;
  // }
  return n;
}

Buffer不是线程安全,但是也不存在其他线程会对其进行读写,因为其所属TcpConnection属于一个io线程,只有该io线程才能调用TcpConnection相关函数