云计算百科
云计算领域专业知识百科平台

【项目日记】仿mudou的高并发服务器 --- 实现缓冲区模块,通用类型Any模块,套接字模块

在这里插入图片描述

一个人知道自己为什么而活,

就可以忍受任何一种生活。

— 尼采 —


✨✨✨项目地址在这里 ✨✨✨

✨✨✨https://gitee.com/penggli_2_0/TcpServer✨✨✨


仿mudou的高并发服务器

  • 1 主从Reactor模型
  • 2 基础功能封装
    • 2.1 缓冲区 Buffer模块
    • 2.2 通用类型 Any类
    • 2.3 套接字 Socket模块

1 主从Reactor模型

这个项目的目标是实现一个可以高效处理请求的服务器,那么对于这样的一个服务器要如何实现呢?

这里采取主从Reactor模型的方案:

  • 主Reactor模型负责对监听套接字进行管理,进行获取连接操作。
  • 从属Reactor负责对连接的数据进行处理,并且为了保证线程安全,每一个从属Reactor都要绑定一个线程,这个连接的任务都在这个线程中完成。
  • 简单来说就是这样的一个模型: 在这里插入图片描述 其中管理连接的对象是Connection对象,这个类是高并发服务器的核心部分,这里包含了对套接字的处理,事件等待等一系列操作!而Connection对象中对于这些操作的管理也要通过其他的对象来进行!

    主要的工作就是搭建起主从Reactor模型,但是这个模型不是一下子就可以写出来的。为了实现主从Reactor模型,我们先需要实现一些基础类,对基础功能进封装,然后对这些功能进行整合,最终实现主从Reactor模型!

    2 基础功能封装

    2.1 缓冲区 Buffer模块

    Buffer模块是⼀个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能:

  • 需要支持字符串读取/写入
  • 需要支持char*缓冲区读取/写入
  • 需要正常按行读取 — 方便解析http请求
  • Buffer模块成员变量很简单:

    • vector<char>容器_buffer: 对内存空间进行管理
    • uint64_t _reader_idx 读偏移:进行读取位置在_buffer容器中的偏移量,即读取的起始位置。
    • uint64_t _writer_idx 写偏移:下一次写入位置在_buffer容器中的偏移量,即写入的起始位置。

    接下来实现一下基础功能:

  • 构造函数:初始化读/写偏移为0 ,容器_buffer初始化一个大小 BUFFER_DEFAULT_SIZE。
  • 获取当前写入起始地址:_buffer空间的起始地址加入写偏移量即写入起始地址。
  • 获取当前读取起始地址: _buffer空间的起始地址加入读偏移量即读取起始地址。
  • 获取缓冲区末尾空闲空间大小:写偏移之后的空闲空间 ,总体空间大小减去写偏移就是写偏移之后的空间大小。
  • 获取缓冲区起始空闲空间大小:读偏移之前的空闲空间,其实就是读偏移的大小。
  • 获取可读数据大小:写偏移减去读偏移就就之间可读空间的大小!
  • 读/写偏移向后移动: * 先根据len判断是否小于可读数据大小 len必须小于可读数据大小,然后移动读偏移。 * 向后移动必须小于当前后边的空闲空间大小,写入数据必须小于缓冲区剩余空间大小,不足就进行扩容!
  • 确保可写空间足够 : * 末尾空闲空间足够 直接返回。 * 末尾空间不足,但算上起始位置的空闲空间大小足够 ,将数据移动到起始位置。 * 如果总空间不足 ,进行扩容,扩容到足够空间即可
  • 写入数据:首先保证有足够空间,然后将数据数据拷贝进去。可以继续设计出针对string的写入、针对Buffer的写入以及写入 + 压入数据
  • 读取数据:首先读取大小len必须小于可读取数据大小,然后拷贝数据出来。同样可以设计出针对string的读取、针对Buffer的读取以及读取+弹出数据。
  • 清空缓冲区:将偏移量归零即可!
  • 读取一行数据:先找到换行符,然后进行读取。
  • // 缓冲区Buffer类
    class Buffer
    {

    private:
    std::vector<char> _buffer;
    uint64_t _reader_idx;
    uint64_t _writer_idx;

    private:
    char *Begin() {
    return &*_buffer.begin(); }

    public:
    Buffer() : _buffer(BUFFER_DEFAULT_SIZE), _reader_idx(0), _writer_idx(0) {
    }
    // 获取当前写入起始地址
    char *WritePos() {
    return Begin() + _writer_idx; }
    // 获取当前读取起始地址
    char *ReadPos() {
    return Begin() + _reader_idx; }
    // 获取读取位置之前的空闲空间
    uint64_t HeadIdleSize() {
    return _reader_idx; }
    // 获取写入位置之后的空闲空间
    uint64_t TailIdleSize() {
    return _buffer.size() _writer_idx; }
    // 获取可读数据大小
    uint64_t ReadAbleSize() {
    return _writer_idx _reader_idx; }
    // 读/写偏移移动
    void MoveReadOffset(uint64_t len)
    {

    if (len <= 0)
    return;
    assert(len <= ReadAbleSize());
    _reader_idx += len;
    }
    void MoveWriteOffset(uint64_t len)
    {

    if (len <= 0)
    return;
    assert(len <= TailIdleSize());
    _writer_idx += len;
    }
    // 确保可写空间足够 — 整体空闲空间足够了就移动数据,否则进行扩容
    void EnsureWriteSpace(uint64_t len)
    {

    // 当len小于写入位置之后的空闲空间 直接进行写入
    if (len <= TailIdleSize())
    {

    return;
    }

    // len 小于总共的空闲空间
    else if (len <= TailIdleSize() + HeadIdleSize())
    {

    // 记录总共的数据
    uint64_t sz = ReadAbleSize();
    // 移动数据
    std::copy(ReadPos(), ReadPos() + sz, Begin());
    // 更新写入读取位置
    _reader_idx = 0;
    _writer_idx = sz;
    }
    // 需要扩容
    else
    {

    // 在写入位置之后扩充len个大小
    _buffer.resize(_writer_idx + len);
    }
    }
    // ———-写入数据————
    void Write(const void *data, uint64_t len)
    {

    // 写入的数据不能超过可写的总空间
    // 确保可以正常写入
    EnsureWriteSpace(len);
    // 进行拷贝
    const char *d = reinterpret_cast<const char *>(data);
    std::copy(d, d + len, WritePos());
    }
    // 写入Buffer
    void WriteBuffer

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【项目日记】仿mudou的高并发服务器 --- 实现缓冲区模块,通用类型Any模块,套接字模块
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!