http服务器_图文详解简易Buffer设计与C++实现(ref muduo)

http服务器_简易Buffer设计与C++实现(ref muduo)

一、Buffer缓冲区存在的意义

  高性能http服务器常见的模型就是基于event loop的reactor反应堆模型,采用IO 复用(linux一般是用epoll) + non-blocking IO 网络编程提高性能。non-blocking编程思想的核心在于避免线程阻塞在read、write等系统调用上,否则对之后的event就不能及时做出响应。IO线程只能阻塞在IO复用的函数上,如epoll_wait,所以每个TCP连接都必须要有input buffer和output buffer。
  例如程序想通过TCP连接发送一个100KB的数据,但是在write函数中,系统只接受了60KB的数据(TCP滑动窗口机制),这时候程序不应持续等待,而是应该尽快返回event loop防止对于后续事件没有响应,对于剩下的40KB数据该作何处理呢?此时这些数据应该保存在output buffer中,然后注册EPOLLOUT事件,等待下次可写,然后发送出去。

二、Buffer缓冲区的功能需求

(1)连续内存方便读写和管理,结构尽量简单
(2)长度可变,可以适应不同大小的消息
(3)尽量减少缓冲区占用的内存

三、Buffer缓冲区的实现代码

#ifndef __BUFFER_H__
#define __BUFFER_H__

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <cassert>

#define INIT_SIZE 1024

namespace huhu{
    
class Buffer{
public:
    Buffer();
    ~Buffer();

    //default copy constructor and assign func.

    size_t readableBytes() const;//可读字节数
    size_t writableBytes() const;//可写字节数
    size_t prependableBytes() const;//先前字节数
    //first readable pos 
    const char* peek() const;//第一个可读位置
	//在可读区域中取出字节
    void retrieveLen(size_t len);//取出len个字节
    void retrieveToEnd(const char* end);//一直取出字节直到end
    void retrieveAll();//取出所有字节
    std::string retrieveAllAsString();//以字符串方式取出所有字节
	//添加字节到缓冲区可写区域中
    void append(const std::string& str);
    void append(const char* data, size_t len);
    void append(const void* data, size_t len);
    void append(const Buffer& other_buff);

    void ensureWritableBytes(size_t len);//确保空间足够
    char* beginWrite();//返回第一个可写位置
    const char* beginWrite() const;
    void hasWritten(size_t len);//调整可写位置
	//通过缓冲区读写fd
    ssize_t readFd(int fd, int* savedErrno);
    ssize_t writeFd(int fd, int* saveErrno);

    const char* findCRLF() const;
    const char* findCRLF(const char* start) const;


private:
    char* __begin();//返回buffer起始地址
    const char* __begin() const;
    void __makeSpace(size_t len);//调整空间

private:
    std::vector<char> m_buffer;//为了便于动态调整大小,使用vector<char>
    size_t m_read_index;//读下标
    size_t m_write_index;//写下标
};//class Buffer

}//namespace huhu

#endif//__BUFFER_H__

在这里插入图片描述

#include "../include/Buffer.h"

#include <cstring> // perror
#include <iostream>
#include <errno.h>
#include <unistd.h> // write
#include <sys/uio.h> // readv

using namespace huhu;

Buffer::Buffer()
    :m_buffer(INIT_SIZE), \
    m_read_index(0), \
    m_write_index(0){
        assert(readableBytes() == 0);
        assert(writableBytes() == INIT_SIZE);
}

Buffer::~Buffer(){}

size_t Buffer::readableBytes() const{
    return m_write_index - m_read_index;
}

size_t Buffer::writableBytes() const{
    return m_buffer.size() - m_write_index;
}

size_t Buffer::prependableBytes() const{
    return m_read_index;
}
//first readable position
const char* Buffer::peek() const {
    return __begin() + m_read_index;
}

void Buffer::retrieveLen(size_t len){
    assert(len <= readableBytes());
    m_read_index += len;
}

void Buffer::retrieveToEnd(const char* end){
    assert(peek() <= end);
    assert(end <= beginWrite());

    retrieveLen(end - peek());
}

void Buffer::retrieveAll(){
    m_read_index = 0;
    m_write_index = 0;
}

std::string Buffer::retrieveAllAsString(){
    std::string str(peek(), readableBytes());
    retrieveAll();
    return str;
}

void Buffer::append(const std::string& str){
    append(str.data(), str.size());
}

void Buffer::append(const char* data, size_t len){
    ensureWritableBytes(len);
    std::copy(data, data + len, beginWrite());
    hasWritten(len);
}

void Buffer::append(const void* data, size_t len){
    append(static_cast<const char*>(data), len);
}

void Buffer::append(const Buffer& other_buff){
    append(other_buff.peek(), other_buff.readableBytes());
}

void Buffer::ensureWritableBytes(size_t len){
    if(writableBytes() < len){
        __makeSpace(len);
    }
    assert(writableBytes() >= len);
}

char* Buffer::beginWrite(){
    return __begin() + m_write_index;
}

const char* Buffer::beginWrite() const{
    return __begin() + m_write_index;
}

void Buffer::hasWritten(size_t len){
    m_write_index += len;
}

ssize_t Buffer::readFd(int fd, int* savedErrno){
    // ref muduo buffer.p204[chenshuo]
    char extrabuf[65536];
    struct iovec vec[2];
    const size_t writable = writableBytes();
    vec[0].iov_base = __begin() + m_write_index;
    vec[0].iov_len = writable;
    vec[1].iov_base = extrabuf;
    vec[1].iov_len = sizeof(extrabuf);
    const ssize_t n = ::readv(fd, vec, 2);
    if(n < 0) {
        printf("[Buffer:readFd]fd = %d readv : %s\n", fd, strerror(errno));
        *savedErrno = errno;
    } 
    else if(static_cast<size_t>(n) <= writable)
        m_write_index += n;
    else {
        m_write_index = m_buffer.size();
        append(extrabuf, n - writable);
    }

    return n;
}

ssize_t Buffer::writeFd(int fd, int* savedErrno){
    size_t nLeft = readableBytes();
    char* bufPtr = __begin() + m_read_index;
    ssize_t n;
    if((n = ::write(fd, bufPtr, nLeft)) <= 0) {
        if(n < 0 && n == EINTR)
            return 0;
        else {
            printf("[Buffer:writeFd]fd = %d write : %s\n", fd, strerror(errno));
            *savedErrno = errno;
            return -1;
        }
    } else {
        m_read_index += n;
        return n;
    }
}

const char* Buffer::findCRLF() const{
    const char CRLF[] = "\r\n";
    const char* crlf = std::search(peek(), beginWrite(), CRLF, CRLF + 2);
    return crlf == beginWrite() ? nullptr : crlf;
}

const char* Buffer::findCRLF(const char* start) const{
    assert(peek() <= start && start <= beginWrite());

    const char CRLF[] = "\r\n";
    const char* crlf = std::search(start, beginWrite(), CRLF, CRLF + 2);
    return crlf == beginWrite() ? nullptr : crlf;
}

char* Buffer::__begin(){
    return &*m_buffer.begin();
}

const char* Buffer::__begin() const{
    return &*m_buffer.begin();
}
//空间配置
void Buffer::__makeSpace(size_t len){
    if(writableBytes() + prependableBytes() < len){
        m_buffer.resize(m_write_index + len);
    }
    else{
        size_t readable = readableBytes();
        std::copy(__begin() + m_read_index, \
                    __begin() + m_write_index, \
                    __begin());
        m_read_index = 0;
        m_write_index = m_read_index + readable;
        assert(readable == readableBytes());
    }
}

四、Buffer缓冲区的关键函数讲解

4.1 从Fd中读到缓冲区函数readFd

关于 struct iovec可以参考GNU readv/writev的相关文档

ssize_t Buffer::readFd(int fd, int* savedErrno){
    // ref muduo buffer.p204[chenshuo]
    
    char extrabuf[65536];//额外的临时栈空间
    struct iovec vec[2];
    const size_t writable = writableBytes();
    vec[0].iov_base = __begin() + m_write_index;
    vec[0].iov_len = writable;
    vec[1].iov_base = extrabuf;
    vec[1].iov_len = sizeof(extrabuf);
    const ssize_t n = ::readv(fd, vec, 2);
    if(n < 0) {
        printf("[Buffer:readFd]fd = %d readv : %s\n", fd, strerror(errno));
        *savedErrno = errno;
    } 
    else if(static_cast<size_t>(n) <= writable)
        m_write_index += n;
    else {
        m_write_index = m_buffer.size();
        append(extrabuf, n - writable);
    }

    return n;
}

  这里在栈上申请了一个大小为65536的临时空间,如果从fd中读入的数据不多,那么就全读入buffer,如果读入的数据超过了buffer的可写字节数writable,就会读到栈上的extrabuf中,然后再把栈里的数据append到buffer中(调整buffer空间再copy)。这样使得一开始buffer的空间不会太大造成浪费,也避免了多次进行read系统调用造成的开销(这里缓冲区较大,一般可以一次读完)。

4.2 空间配置函数__makeSpace

void Buffer::__makeSpace(size_t len){
    if(writableBytes() + prependableBytes() < len){
        m_buffer.resize(m_write_index + len);
    }
    else{
        size_t readable = readableBytes();
        std::copy(__begin() + m_read_index, \
                    __begin() + m_write_index, \
                    __begin());
        m_read_index = 0;
        m_write_index = m_read_index + readable;
        assert(readable == readableBytes());
    }
}

这里如果writable空间加上prependable空间大于等于len的话,就移动数据位置到缓冲区的最前面。

移动前:
在这里插入图片描述
移动后:
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值