muduo库阅读(23)——Net部分:应用层缓冲区类

来源:互联网 发布:nodejs mysql 事务 编辑:程序博客网 时间:2024/05/21 09:44
/* * 应用层缓冲区(专门用于网络数据读取发送) * 假如这个buffer用于套接字读取,那么框架把套接字读取的数据写入到writerIndex指示的位置,而用户则从readerIndex的位置开始读取数据 * readerIndex和writerIndex之间的这段存放了有效数据 */namespace muduo{namespace net{// 一个缓冲区由三个部分组成:预留区、读取区,写入区,// 0~readerIndex之间是预留区// readerIndex~writerIndex之间是读取区// writerIndex~size之间是写入区/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer////// @code/// 预留区 可读数据可写数据/// +-------------------+------------------+------------------+/// | prependable bytes |  readable bytes  |  writable bytes  |/// |                   |     (CONTENT)    |                  |/// +-------------------+------------------+------------------+/// |                   |                  |                  |/// 0      <=      readerIndex   <=   writerIndex    <=     size/// @endcodeclass Buffer : public muduo::copyable{public:static const size_t kCheapPrepend = 8;/* * 缓默认的冲区初始大小 * 起始的时候,预留区为8字节 * 读取区(或可读区域)为0字节 * 写入区为kInitialSize字节 */static const size_t kInitialSize = 1024;/* * 初始化构造 */explicit Buffer(size_t initialSize = kInitialSize): buffer_(kCheapPrepend + initialSize),  readerIndex_(kCheapPrepend),  writerIndex_(kCheapPrepend){assert(readableBytes() == 0);assert(writableBytes() == initialSize);assert(prependableBytes() == kCheapPrepend);}// implicit copy-ctor, move-ctor, dtor and assignment are fine// NOTE: implicit move-ctor is added in g++ 4.6// 数据交换void swap(Buffer& rhs){buffer_.swap(rhs.buffer_);std::swap(readerIndex_, rhs.readerIndex_);std::swap(writerIndex_, rhs.writerIndex_);}// 可读区域的字节数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_; }// 在可读区域查找\r\nconst char* findCRLF() const{// FIXME: replace with memmem()?const char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF+2);return crlf == beginWrite() ? NULL : crlf;}// 在可读区域指定的起始位置查找\r\nconst char* findCRLF(const char* start) const{assert(peek() <= start);assert(start <= beginWrite());// FIXME: replace with memmem()?const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);return crlf == beginWrite() ? NULL : crlf;}// 在可读区域查找\n// EOL即一行的结束const char* findEOL() const{const void* eol = memchr(peek(), '\n', readableBytes());return static_cast<const char*>(eol);}// 在可读区域指定的起始位置查找\nconst char* findEOL(const char* start) const{assert(peek() <= start);assert(start <= beginWrite());const void* eol = memchr(start, '\n', beginWrite() - start);return static_cast<const char*>(eol);}// retrieve returns void, to prevent// string str(retrieve(readableBytes()), readableBytes());// the evaluation of two functions are unspecified// 在可读区域获取一定的数据(空间),用于数据读取// 一般的用法是先从可读区域获取数据,然后在调用这个函数,移动可读区域的指针// 移动可读区域的指针void retrieve(size_t len){// 一般来说都不会出现len > readableBytes()的情况assert(len <= readableBytes());if (len < readableBytes()){readerIndex_ += len;}else{retrieveAll();}}// 把可读区域的指针移动到指定位置void retrieveUntil(const char* end){assert(peek() <= end);assert(end <= beginWrite());retrieve(end - peek());}void retrieveInt64(){retrieve(sizeof(int64_t));}void retrieveInt32(){retrieve(sizeof(int32_t));}void retrieveInt16(){retrieve(sizeof(int16_t));}void retrieveInt8(){retrieve(sizeof(int8_t));}// 把可读区域的数据全部取出void retrieveAll(){readerIndex_ = kCheapPrepend;writerIndex_ = kCheapPrepend;}// 把可读区域的数据全部取出当作字符串string retrieveAllAsString(){return retrieveAsString(readableBytes());;}// 把指定长度的数据取出当作字符串string retrieveAsString(size_t len){assert(len <= readableBytes());string result(peek(), len);retrieve(len);return result;}// 同retrieveAllAsString,不过StringPiece并不真正存储字符串,他只是一个包装// 这个函数也可以说是把可读区域的数据都当成字节序列,不区分类型StringPiece toStringPiece() const{return StringPiece(peek(), static_cast<int>(readableBytes()));}// 写入数据void append(const StringPiece& str){append(str.data(), str.size());}// 写入数据void append(const char* /*restrict*/ data, size_t len){// 确保写入区的长度ensureWritableBytes(len);// 数据复制到写入区std::copy(data, data+len, beginWrite());// 移动写入区的指针hasWritten(len);}// 写入数据void append(const void* /*restrict*/ data, size_t len){append(static_cast<const char*>(data), len);}// 确保写入区域的大小void ensureWritableBytes(size_t len){if (writableBytes() < len){// 扩展空间makeSpace(len);}assert(writableBytes() >= len);}// 获取写入区的起始指针char* beginWrite(){ return begin() + writerIndex_; }// 获取写入区的起始指针const char* beginWrite() const{ return begin() + writerIndex_; }// 移动写入区的指针(往前移动,即写入数据)void hasWritten(size_t len){assert(len <= writableBytes());writerIndex_ += len;}// 移动写入区的指针(往后移动,即回退数据)void unwrite(size_t len){assert(len <= readableBytes());writerIndex_ -= len;}// 写入一个int64_t类型的数据,先把数据转换成网络字节顺序,再写入// 同时移动写入区的指针void appendInt64(int64_t x){int64_t be64 = sockets::hostToNetwork64(x);append(&be64, sizeof be64);}// 写入一个int32_t类型的数据void appendInt32(int32_t x){int32_t be32 = sockets::hostToNetwork32(x);append(&be32, sizeof be32);}// 写入一个int16_t类型的数据void appendInt16(int16_t x){int16_t be16 = sockets::hostToNetwork16(x);append(&be16, sizeof be16);}// 写入一个int8_t类型的数据void appendInt8(int8_t x){append(&x, sizeof x);}// 读取一个int64_t类型的数据,先将数据从网络字节顺序转换成主机字节顺序,然后再返回// 同时移动读取区的指针int64_t readInt64(){int64_t result = peekInt64();retrieveInt64();return result;}// 读取一个int32_t类型的数据int32_t readInt32(){int32_t result = peekInt32();retrieveInt32();return result;}// 读取一个int16_t类型的数据int16_t readInt16(){int16_t result = peekInt16();retrieveInt16();return result;}// 读取一个int8_t类型的数据int8_t readInt8(){int8_t result = peekInt8();retrieveInt8();return result;}// 从buffer中取出一个int64_t类型的数据,然后转换成主机字节顺序int64_t peekInt64() const{assert(readableBytes() >= sizeof(int64_t));int64_t be64 = 0;::memcpy(&be64, peek(), sizeof be64);return sockets::networkToHost64(be64);}// 从buffer中取出一个int32_t类型的数据,然后转换成主机字节顺序int32_t peekInt32() const{assert(readableBytes() >= sizeof(int32_t));int32_t be32 = 0;::memcpy(&be32, peek(), sizeof be32);return sockets::networkToHost32(be32);}// 从buffer中取出一个int16_t类型的数据,然后转换成主机字节顺序int16_t peekInt16() const{assert(readableBytes() >= sizeof(int16_t));int16_t be16 = 0;::memcpy(&be16, peek(), sizeof be16);return sockets::networkToHost16(be16);}// 从buffer中取出一个int8_t类型的数据,然后转换成主机字节顺序int8_t peekInt8() const{assert(readableBytes() >= sizeof(int8_t));int8_t x = *peek();return x;}// 写入int64_t类型的数据,先把它转换成网络字节顺序。注意:数据是写入可读区域的前面(readerIndex_之前),这和append不同!!!void prependInt64(int64_t x){int64_t be64 = sockets::hostToNetwork64(x);prepend(&be64, sizeof be64);}// 写入int32_t类型的数据,先把它转换成网络字节顺序。注意:数据是写入可读区域的前面(readerIndex_之前),这和append不同!!!void prependInt32(int32_t x){int32_t be32 = sockets::hostToNetwork32(x);prepend(&be32, sizeof be32);}// 写入int16_t类型的数据,先把它转换成网络字节顺序。注意:数据是写入可读区域的前面(readerIndex_之前),这和append不同!!!void prependInt16(int16_t x){int16_t be16 = sockets::hostToNetwork16(x);prepend(&be16, sizeof be16);}// 写入int8_t类型的数据,先把它转换成网络字节顺序。注意:数据是写入可读区域的前面(readerIndex_之前),这和append不同!!!void prependInt8(int8_t x){prepend(&x, sizeof x);}// 与append相对应,append在writerIndex_所指向的缓冲区写入数据(即在可读区域后面写入数据)// 而prepend则是在readerIndex_所指向的缓冲区的前面写入数据(即在可读区域前面写入数据)void prepend(const void* /*restrict*/ data, size_t len){assert(len <= prependableBytes());readerIndex_ -= len;const char* d = static_cast<const char*>(data);std::copy(d, d+len, begin()+readerIndex_);}// 空间扩展,在原来的基础上增加reserve字节空间void shrink(size_t reserve){// FIXME: use vector::shrink_to_fit() in C++ 11 if possible.Buffer other;other.ensureWritableBytes(readableBytes()+reserve);// 复制原来的数据other.append(toStringPiece());// 交换swap(other);}// 初始化缓冲区容量size_t internalCapacity() const{return buffer_.capacity();}// 从套接字(文件描述符)中读取数据,然后存放在缓冲区中,savedErrno保存了错误码ssize_t readFd(int fd, int* savedErrno);private:// 缓冲区的起始位置char* begin(){ return &*buffer_.begin(); }// 缓冲区的起始位置const char* begin() const{ return &*buffer_.begin(); }// 分配空间(或者调整空间)void makeSpace(size_t len){// 空间不足的情况下需要重新分配空间if (writableBytes() + prependableBytes() < len + kCheapPrepend){// FIXME: move readable databuffer_.resize(writerIndex_+len);}// 总的剩余空间还足够,但是需要整理以方便使用else{// move readable data to the front, make space inside buffer// readerIndex_前面的空间太多,而writerIndex_之后的空间又不足// 需要将readerIndex_与writerIndex_之间的数据往前面移动,让writerIndex_之后有足够的空间assert(kCheapPrepend < readerIndex_);size_t readable = readableBytes();std::copy(begin()+readerIndex_,begin()+writerIndex_,begin()+kCheapPrepend);readerIndex_ = kCheapPrepend;writerIndex_ = readerIndex_ + readable;assert(readable == readableBytes());}}private:// 缓冲区std::vector<char> buffer_;// 读写指针size_t readerIndex_;size_t writerIndex_;// 回车换行符static const char kCRLF[];};}}

/* * 从套接字中直接读取数据,然后存放在缓冲区中 */ssize_t Buffer::readFd(int fd, int* savedErrno){// saved an ioctl()/FIONREAD call to tell how much to readchar 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.// 如果可写区域的剩余空间大于65536,那么把数据直接写到写入区即可,否则还需要一个额外的扩展空间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);}// if (n == writable + sizeof extrabuf)// {//   goto line_30;// }return n;}


0 0
原创粉丝点击