-
应用层缓冲区Buffer设计
-
为什么需要有应用层缓冲区(详见MuduoManual.pdf P76)
-
Buffer结构
-
epoll使用LT模式的原因
-
与poll兼容
-
LT模式不会发生漏掉事件的BUG,但POLLOUT事件不能一开始就关注,否则会出现busy loop,而应该在write无法完全写入内核缓冲区的时候才关注,将未写入内核缓冲区的数据添加到应用层output buffer,直到应用层output buffer写完,停止关注POLLOUT事件。
读写的时候不必等候EAGAIN,可以节省系统调用次数,降低延迟。(注:如果用ET模式,读的时候读到EAGAIN,写的时候直到output buffer写完或者EAGAIN)
BUFFER头文件
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
/*
接收到数据,存至input buffer,通知上层的应用程序,OnMessage(Buffer *buf)回调,根据应用层协议
判断是否一个完整的包,codec,如果不是一条完整的消息,不会取走数据,也不会进行相应的处理,
如果是一条完整的信息,将取走这条信息,并进行相应的处理
对方发送了两条消息,50,350
接收方第一次接收了200,第二次接收了200
从socket读了 200 个字节,写到buffer中
从buffer中取回50 个字节
又从socket读了200个字节,写到buffer中
从buffer中取回350个字节,写到buffer中
从buffer中取回350个字节
*/
#ifndef MUDUO_NET_BUFFER_H
#define MUDUO_NET_BUFFER_H
#include <muduo/base/copyable.h>
#include <muduo/base/StringPiece.h>
#include <muduo/base/Types.h>
#include <muduo/net/Endian.h>
#include <algorithm>
#include <vector>
#include <assert.h>
#include <string.h>
//#include <unistd.h> // ssize_t
namespace muduo
{
namespace net
{
/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
///
/// @code
/// +-------------------+------------------+------------------+
/// | prependable bytes | readable bytes | writable bytes |
/// | | (CONTENT) | |
/// +-------------------+------------------+------------------+
/// | | | |
/// 0 <= readerIndex <= writerIndex <= size
/// @endcode
class Buffer : public muduo::copyable
{
public:
static const size_t kCheapPrepend = 8;
static const size_t kInitialSize = 1024;
Buffer()
/*缓冲区的大小*/
: buffer_(kCheapPrepend + kInitialSize),
/*可读的开始位置*/
readerIndex_(kCheapPrepend),
/*可写的开始位置*/
writerIndex_(kCheapPrepend)
{
assert(readableBytes() == 0);
assert(writableBytes() == kInitialSize);
assert(prependableBytes() == kCheapPrepend);
}
// default copy-ctor(拷贝构造函数), dtor and assignment are fine
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_; }
const char* findCRLF() const
{
const char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF+2);
return crlf == beginWrite() ? NULL : crlf;
}
const char* findCRLF(const char* start) const
{
assert(peek() <= start);
assert(start <= beginWrite());
const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);
return crlf == beginWrite() ? NULL : crlf;
}
// retrieve returns void, to prevent
// string str(retrieve(readableBytes()), readableBytes());
// the evaluation of two functions are unspecified
/*取回数据*/
void retrieve(size_t len)
{
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 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;
}
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);
}
// 确保缓冲区可写空间>=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)
{ writerIndex_ += len; }
///
/// Append int32_t using network endian
/// 使用网络字节序追加到buffer
void appendInt32(int32_t x)
{
int32_t be32 = sockets::hostToNetwork32(x);
append(&be32, sizeof be32);
}
void appendInt16(int16_t x)
{
int16_t be16 = sockets::hostToNetwork16(x);
append(&be16, sizeof be16);
}
void appendInt8(int8_t x)
{
append(&x, sizeof x);
}
///
/// Read int32_t from network endian
///
/// Require: buf->readableBytes() >= sizeof(int32_t)
int32_t readInt32()
{
int32_t result = peekInt32();
retrieveInt32();
return result;
}
int16_t readInt16()
{
int16_t result = peekInt16();
retrieveInt16();
return result;
}
int8_t readInt8()
{
int8_t result = peekInt8();
retrieveInt8();
return result;
}
///
/// Peek int32_t from network endian
///
/// Require: buf->readableBytes() >= sizeof(int32_t)
int32_t peekInt32() const
{
assert(readableBytes() >= sizeof(int32_t));
int32_t be32 = 0;
::memcpy(&be32, peek(), sizeof be32);
return sockets::networkToHost32(be32);
}
int16_t peekInt16() const
{
assert(readableBytes() >= sizeof(int16_t));
int16_t be16 = 0;
::memcpy(&be16, peek(), sizeof be16);
return sockets::networkToHost16(be16);
}
int8_t peekInt8() const
{
assert(readableBytes() >= sizeof(int8_t));
int8_t x = *peek();
return x;
}
///
/// Prepend int32_t using network endian
///
void prependInt32(int32_t x)
{
int32_t be32 = sockets::hostToNetwork32(x);
prepend(&be32, sizeof be32);
}
void prependInt16(int16_t x)
{
int16_t be16 = sockets::hostToNetwork16(x);
prepend(&be16, sizeof be16);
}
void prependInt8(int8_t x)
{
prepend(&x, sizeof x);
}
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);
}
/// Read data directly into buffer.
///
/// It may implement with readv(2)
/// @return result of read(2), @c errno is saved
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 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());
}
}
private:
std::vector<char> buffer_; // vector用于替代固定大小数组
size_t readerIndex_; // 读位置
size_t writerIndex_; // 写位置
static const char kCRLF[]; // "\r\n"
};
}
}
#endif // MUDUO_NET_BUFFER_HBUFFER源文件
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
#include <muduo/net/Buffer.h>
#include <muduo/net/SocketsOps.h>
#include <errno.h>
#include <sys/uio.h>
using namespace muduo;
using namespace muduo::net;
const char Buffer::kCRLF[] = "\r\n";
const size_t Buffer::kCheapPrepend;
const size_t Buffer::kInitialSize;
// 结合栈上的空间,避免内存使用过大,提高内存使用率
// 如果有5K个连接,每个连接就分配64K+64K的缓冲区的话,将占用640M内存,
// 而大多数时候,这些缓冲区的使用率很低
ssize_t Buffer::readFd(int fd, int* savedErrno)
{
// saved an ioctl()/FIONREAD call to tell how much to read
// 节省一次ioctl系统调用(获取有多少可读数据)
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;
const ssize_t n = sockets::readv(fd, vec, 2);
if (n < 0)
{
*savedErrno = errno;
}
else if (implicit_cast<size_t>(n) <= writable) //第一块缓冲区足够容纳
{
writerIndex_ += n;
}
else // 当前缓冲区,不够容纳,因而数据被接收到了第二块缓冲区extrabuf,将其append至buffer
{
writerIndex_ = buffer_.size();
append(extrabuf, n - writable);
}
// if (n == writable + sizeof extrabuf)
// {
// goto line_30;
// }
return n;
}
1193

被折叠的 条评论
为什么被折叠?



