为什么non-blocking 网络编程中应用层Buffer是必须的?
TODO
Buffer 数据结构
class Buffer
{
public:
static const size_t kCheapPrepend = 8;
static const size_t kInitialSize = 1024;
public:
private:
std::vector<char> buffer_;
size_t readerIndex_;
size_t writerIndex_;
};
image.png
-
为什么
readerIndex_
和writerIndex_
是int
,不是char*
?
应对迭代器失效 -
readerIndex_ 和 writeIndex_ 满足不变式
-
底层为什么采用std::vector
- 内存自动增长
- 指数增长的内存减少内存分配次数
- size() 自适应
Buffer 操作
size_t readableBytes() const { return writerIndex_ - readerIndex_; }
size_t writableBytes() const { return buffer_.size() - writerIndex_; }
size_t prependableBytes() const { return readerIndex_; }
const char *peek() const { return begin() + readerIndex_; }
char *beginWrite() { return begin() + writerIndex_; }
写入数据
void appendInt32(int32_t x)
{
int32_t be32 = sockets::hostToNetwork32(x);
append(&be32, sizeof(be32));
}
void append(const void * /*restrict*/ data, size_t len)
{
append(static_cast<const char *>(data), len);
}
void append(const char * /*restrict*/ data, size_t len)
{
ensureWritableBytes(len);
std::copy(data, data + len, beginWrite());
hasWritten(len);
}
void ensureWritableBytes(size_t len)
{
if (writableBytes() < len)
{
makeSpace(len);
}
assert(writableBytes() >= len);
}
void makeSpace(size_t len)
{
if (writableBytes() + prependableBytes() < len + kCheapPrepend)
{
// FIXME: move readable data
buffer_.resize(writerIndex_ + len);
}
else
{
// move readable data to the front, make space inside buffer
assert(kCheapPrepend < readerIndex_);
size_t readable = readableBytes();
std::copy(begin() + readerIndex_,
begin() + writerIndex_,
begin() + kCheapPrepend);
readerIndex_ = kCheapPrepend;
writerIndex_ = readerIndex_ + readable;
assert(readable == readableBytes());
}
}
void hasWritten(size_t len)
{
assert(len <= writableBytes());
writerIndex_ += len;
}
-
内部腾挪
image.png
有时候,经过若干次读写,当readIndex_
移动到了比较靠后的位置,留下了巨大的prependable
空间。
如图所示:这时如果我们想写入300字节,而
image.pngwriteable
只有200字节,这时,Buffer 并不会重新分配内存。而是把已有的数据移动到前面去。腾出writeable
空间
-
std::vector resize 和 reserve 区别 ?
- reserver函数用来给vector预分配存储区大小,即capacity的值 ,但是没有给这段内存进行初始化。
- resize函数重新分配大小,改变容器的大小,并且创建对象
- 当 n 小于当前 size() 值时候,vector 首先会减少 size() 值 保存前 n 个元素,然后将超出 n 的元素删除 (remove and destroy)
- 当 n 大于当前 size() 值时候,vector 会插入相应数量的元素 使得 size() 值达到 n,并对这些元素进行初始化,如果调用上面的第二个 resize 函数,指定 val,vector 会用 val 来初始化这些新插入的元素
- 当 n 大于 capacity() 值的时候,会自动分配重新分配内存存储空间。
读取数据
int32_t readInt32()
{
int32_t result = peekInt32();
retrieveInt32();
return result;
}
void retrieveInt32()
{
retrieve(sizeof(int32_t));
}
void retrieve(size_t len)
{
assert(len <= readableBytes());
if (len < readableBytes())
{
readerIndex_ += len;
}
else // len == readableBytes()
{
retrieveAll();
}
}
void retrieveAll()
{
readerIndex_ = kCheapPrepend;
writerIndex_ = kCheapPrepend;
}
prehead
readFd
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;
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);
}
return n;
}
网友评论