重写muduo之Buffer

1、 Buffer.h

Buffer封装

是一个缓冲区

prependable bytes+readable bytes+writable bytes=8字节长度(解决粘包问题)+读数据+写数据
根据下标进行读或者写

3个成员变量:数组,数据可读的下标,数据可写的下标

#pragma once

#include <vector>
#include <string>
#include <algorithm>

//网络库底层的缓冲区类型定义
class Buffer
{
public:
    static const size_t kCheapPrepend=8;//记录数据包的长度
    static const size_t kInitialSize=1024;//缓冲区的大小

    explicit Buffer(size_t initialSize=kInitialSize)
        :buffer_(kCheapPrepend+initialSize)
        ,readerIndex_(kCheapPrepend)
        ,writerIndex_(kCheapPrepend)
    {}

    size_t readableBytes() const//可读的缓冲区大小
    {
        return writerIndex_-readerIndex_;
    }

    size_t writableBytes() const//可写的缓冲区大小
    {
        return buffer_.size()-writerIndex_;
    }

    size_t prependableBytes() const//prependable Bytes区域大小
    {
        return readerIndex_;
    }

    //返回缓冲区中可读数据的起始地址
    const char* peek() const
    {
        return begin()+readerIndex_;
    }

    //注册回调  onMessage 有读写事件发生时,将buffer->string
    void retrieve(size_t len)
    {
        if(len<readableBytes())
        {
            readerIndex_+=len;//应用只读取了可读缓冲区数据的一部分,就是len,还剩下readerIndex_+=len~writerIndex_没读
        }
        else//len==readableBytes()
        {
            retrieveAll();
        }
    }

    void retrieveAll()
    {
        readerIndex_=writerIndex_=kCheapPrepend;
    }

    //把onMessage函数上报的Buffer数据,转成string类型的数据返回
    std::string retrieveAllAsString()
    {
        return retrieveAllAsString(readableBytes());//应用可读取数据的长度
    }

    std::string retrieveAllAsString(size_t len)
    {
        std::string result(peek(),len);//peek() 可读数据的起始地址
        retrieve(len);//上面一句把缓冲区中可读的数据,已经读取出来,这里肯定要对缓冲区进行复位操作
        return result;
    }

    //剩余的可写缓冲区buffer_.size()~writerIndex_    要写的数据的长度  len
    void ensureWriteableBytes(size_t len)
    {
        if(writableBytes()<len)
        {
            makeSpace(len);//扩容函数
        }
    }

    //把[data,data+len]内存上的数据,添加到writable缓冲区当中
    void append(const char* data,size_t len)
    {
        ensureWriteableBytes(len);//确保空间可用
        std::copy(data,data+len,beginWrite());
        writerIndex_+=len;
    }

    char* beginWrite()//可以写的地方的地址
    {
        return begin()+writerIndex_;
    }

    const char* beginWrite()const//常对象可调用
    {
        return begin()+writerIndex_;
    }

    //从fd上读取数据
    ssize_t readFd(int fd,int* saveErrno);

    //通过fd发送数据
    ssize_t writeFd(int fd,int* saveErrno);

private:
    char* begin()
    {
        //it.operator*()  取迭代器访问的第一个元素,再对这个元素取地址
        return &*buffer_.begin();//vector底层数组首元素的地址,也就是数组的起始地址
    }
    const char* begin() const
    {
        return &*buffer_.begin();
    }

    void makeSpace(size_t len)
    {
        /*
        kCheapPrepend  |  reader    |     writer     |
        kCheapPrepend  |           len               |
        */
        if(writableBytes()+prependableBytes()<len+kCheapPrepend)//writableBytes()+prependableBytes()=>可写缓存区长度+已读完数据已经空闲下来的缓冲区
        {
            buffer_.resize(writerIndex_+len);//扩容
        }
        else//将未读数据和可写缓冲区往前挪
        {
            size_t readalbe=readableBytes();//未读数据长度
            std::copy(begin()+readerIndex_,//将未读数据挪到前面
                    begin()+writerIndex_,
                    begin()+kCheapPrepend);
            readerIndex_=kCheapPrepend;//readerIndex_前移
            writerIndex_=readerIndex_+readalbe;//writerIndex_前移
        }
    }
    std::vector<char> buffer_;
    size_t readerIndex_;
    size_t writerIndex_;
};

2、Buffer.cc 

readv()可以根据读出的数据,自动填充多个缓冲区,这些缓冲区不需要连续
iovec:缓冲区的起始地址,缓冲区的长度

#include "Buffer.h"

#include <errno.h>
#include <sys/uio.h>
#include <unistd.h>

/**
 * 从fd上读取数据  Poller工作在LT模式
 * Buffer缓冲区是有大小的!  但是从fd上读数据的时候  却不知道tcp数据最终的大小
*/
ssize_t Buffer::readFd(int fd,int* saveErrno)
{
    char extrabuf[65536]={0};//栈上的内存空间  64k

    struct iovec vec[2];

    const size_t writable=writableBytes();//这是Buffer底层缓冲区剩余的可写空间大小
    vec[0].iov_base=begin()+writerIndex_;//缓冲区的起始地址
    vec[0].iov_len=writable;//缓冲区的长度

    //如果vec[0]缓冲区够填的话,就填充到vec[0],不够填,就填到vec[1],最后,如果我们看到vec[1]中有内容的话,就把其中的内容直接添加到缓冲区中
    //缓冲区刚刚好存入所有我们需要写入的内容,不浪费空间,空间利用率高
    vec[1].iov_base=extrabuf;
    vec[1].iov_len=sizeof extrabuf;

    const int iovcnt=(writable<sizeof extrabuf)?2:1;
    const ssize_t n=::readv(fd,vec,iovcnt);
    if(n<0)
    {
        *saveErrno=errno;
    }
    else if(n<=writable)//Buffer的可写缓冲区已经够存储读出来的数据了
    {
        writerIndex_+=n;
    }
    else//extrabuf里面也写入了数据
    {
        writerIndex_=buffer_.size();
        append(extrabuf,n-writable);//writerIndex_开始写n-writable大小的数据
    }
    
    return n;//读取的字节数
}

// 通过fd发送数据
ssize_t Buffer::writeFd(int fd, int *saveErrno)
{
    ssize_t n=::write(fd,peek(),readableBytes());
    if(n<0)//错误
    {
        *saveErrno=errno;
    }
    return n;
}

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值