1. 说明
-
一个Buffer类
-
非阻塞网络编程中应用层buffer是必须的,要让程序在write操作上不阻塞,网络库必须给每个tcp connection配置output buffer.
-
在TcpConnection中使用,有两个,分别是inputbuffer和outputbuffer,TcpConnection会从socket读取数据,然后写入inputbuffer,客户代码从inputbuffer读取数据.对于outputbuffer,客户代码会把数据写入outputbuffer,TcpConnection从outputbuffer读取数据并写入socket
-
epoll使用LT模式的原因
- 与poll兼容
- LT模式不会发生漏掉事件的BUG,但POLLOUT事件不能一开始就关注,否则会出现busy loop,而应该在write无法完全写入内核缓冲区的时候才关注,将未写入内核缓冲区的数据添加到应用层output buffer,直到应用层output buffer写完,停止关注POLLOUT事件
- 读写的时候不必等候EAGAIN,可以节省系统调用次数,降低延迟(注:如果用ET模式,读的时候读到EAGAIN,写的时候知道output buffer 写完或者EAGAIN)
2. 变量
1. 私有
-
std::vector buffer_;
- char数组
-
size_t readerIndex_;
- 读位置下标
-
size_t writerIndex_;
- 写位置下标
-
static const char kCRLF[];
- 就是存储’\r\n’
2. 公有
-
static const size_t kCheapPrepend = 8;
- 这个是数据前预留的空间,可以让程序以很低的代价在数据前添加几个字节,比如在前面加几个字节表示消息的长度
-
static const size_t kInitialSize = 1024
- buffer_初始化大小
3. 函数
1. 私有
-
char* begin()
-
const char* begin() const
- 以上两个返回buffer_.begin()
-
void makeSpace(size_t len)
- 对空间的动态调整,如果读位置前面可用的空间+写位置后面可用的空间够用的话,把数据复制到开始,如果不够用的话,再在后面动态增加空间
2. 公有
-
构造
- buffer_和读写位置的初始化
-
void swap(Buffer& rhs)
- 交换缓冲区
-
size_t readableBytes() const
- 可读的数据大小,写位置-读位置
-
size_t writableBytes() const
- 可写的空间大小,buffer_大小-写位置
-
size_t prependableBytes() const
- 返回readerIndex_,表示之前有多大的空间是空的
-
const char* peek() const
- 返回读位置的指针
-
const char* findCRLF() const
- 这个就是在读位置和写位置之间的数据中找’\r\n’换行,找到的话返回这个指针,没找到返回NULL
-
const char* findCRLF(const char* start) const
- 这个是从start位置开始查找
-
const char* findEOL() const
- 使用memechr()查找’\n’,这个是从读位置后面的readableBytes()个字符中找’\n’
-
const char* findEOL(const char* start) const
- 从start位置开始找,功能如上
-
void retrieve(size_t len)
- 就是取完数据后(在其他函数中实现,),这个只移动读指针
- 如果读指针和写指针重叠,也就是数据取完了,就都置为初始位置.
-
void retrieveUntil(const char* end)
- 这个是参数为指针,把这个指针之前的数据读取完,移动读指针
-
void retrieveInt64()
-
void retrieveInt32()
-
void retrieveInt16()
-
void retrieveInt8()
- 上面四个分别是取出8,4,2,1字节,retrieve*系列函数都比较简单,最好是自己看代码理解
-
void retrieveAll()
- 把读写下标置为初始位置
-
string retrieveAllAsString()
-
string retrieveAsString(size_t len)
- 以上两个,是吧读取的数据转换成字符串返回,并调用retrieve移动读下标
-
StringPiece toStringPiece() const
- 把可读数据转换为StringPiece类型返回
-
void append(const StringPiece& str)
-
void append(const char* /*restrict*/ data, size_t len)
-
void append(const void* /*restrict*/ data, size_t len)
- 以上三个就是把数据添加到buffer_缓冲区
-
void ensureWritableBytes(size_t len)
- 就是确保缓冲区空间够写,动态增长空间
-
char* beginWrite()
-
const char* beginWrite() const
- 上面两个返回写位置的指针
-
void hasWritten(size_t len)
- 移动写位置下标,+len
-
void unwrite(size_t len)
- 写位置下标-len
-
void appendInt64(int64_t x)
-
void appendInt32(int32_t x)
-
void appendInt16(int16_t x)
-
void appendInt8(int8_t x)
- 以上四个把主机字节序的转换成网络字节序追加到缓冲区后面
-
int64_t readInt64()
-
int32_t readInt32()
-
int16_t readInt16()
-
int8_t readInt8()
- 读取这些类型的数据返回,并移动读下标,代码比较好理解
-
int64_t peekInt64() const
-
int32_t peekInt32() const
-
int16_t peekInt16() const
-
int8_t peekInt8() const
- 把可读缓冲区中相应字节大小的数据读出来返回
-
void prependInt64(int64_t x)
-
void prependInt32(int32_t x)
-
void prependInt16(int16_t x)
-
void prependInt8(int8_t x)
-
void prepend(const void* /*restrict*/ data, size_t len)
- 在可读数据前面增加一些数据
-
void shrink(size_t reserve)
- 缩小空间,使得空间为可读数据+reserve的大小
-
size_t internalCapacity() const
- 返回buffer_的capacity()
-
ssize_t readFd(int fd, int* savedErrno);
- 从套接字读取数据然后添加到缓冲区中,使用栈上的空间,避免内存使用过大,并且一次尽可能读取更多的数据
- vec[2]为两块缓冲区,第一块指向写的位置,第二块指向栈上的缓冲区,如果第一块缓冲区足够容纳缓冲区数据,就直接返回了.否则才使用第二块缓冲区,即栈上的空间,在append()到缓冲区,这样就提高了内存使用效率,如果一开始就直接分配64k空间的话,可能用不到,就浪费空间了.
- 这里使用了自己封装的readv,实际还是调用标准的readv(),这里的第三个参数是指明有多少块内存数据需要从fd读出.如果可写空间<64k的话,这里就填2,否则就填1.这里这个判断不咋理解,不明白为什么这样判断