仿muduo库的高并发服务器——buffer缓冲区模块、socket模块

本期我们就这着来深入学习该项目

具体源码实现如下:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可

目录

buffer缓冲区模块

源码

socket模块

源码


buffer缓冲区模块

提供的功能:存储数据,取出数据实现思想:

  1. 实现缓冲区有一块内存空间,采用 vector<char>vector 底层其实使用的就是一个线性的内存空间
  2. 要素:
  3. 默认的空间大小;
  4. 当前的读取数据位置
  5. 当前的写入数据位置

操作:

  1. 写入数据:当前写入位置指向哪里,就从哪里开始写入。如果后续剩余空闲空间不够了就扩容

  2. 考虑整体缓冲区空闲空间是否足够(因为该位置也会向后偏移,前边有可能会有空闲空间)

足够 :将数据移动到起始位置即可

不够 :扩容,从当前写位置开始扩容足够大小

数据一旦写入成功,当前写位置,就要向后偏移

  1. 读取数据:

当前的读取位置指向哪里,就从哪里开始读取,前提是有数据可读

可读数据大小:当前写入位置,减去当前读取位置

源码

buffer.hpp

cpp 复制代码
#pragma once
#include <cstdint>
#include <vector>
#include <cassert>
#include <algorithm>
#include <string>
namespace ImMuduo
{
    const size_t DefaultBufferSize = 1024;
    class Buffer
    {
        private:
            std::vector<char> buffer_;
            uint64_t read_;
            uint64_t write_;
        public:
            Buffer();
            char* begin();
            const char* begin() const;
            // 获取当前写入起始地址
            char *WritePos();
            // 获取当前读取起始地址
            const char *ReadPos() const;
            // 获取前沿空闲空间大小--写偏移之后的空闲空间(前沿)
            uint64_t FrontFreeSize();
            // 获取后沿空闲空间大小--读偏移之前的空闲空间(后沿)
            uint64_t BackFreeSize();
            // 获取可读数据大小
            uint64_t ReadableSize() const;
            // 将读偏移向后移动
            void MoveReadPos(uint64_t size);
            // 将写偏移向后移动
            void MoveWritePos(uint64_t size);
            // 确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)
            void EnsureWriteable(uint64_t size);
            // 写入数据
            void Write(const char *data, uint64_t size);
            // 写入数据并移动写偏移
            void WriteAndPush(const char *data, uint64_t size);
            // 字符串形式的写入数据
            void WriteString(const std::string &str);
            // 字符串形式的写入数据并移动写偏移
            void WriteStrAndPush(const std::string &str);
            // 写入缓冲区数据
            void WriteBuffer(const Buffer &buffer);
            // 写入缓冲区数据并移动写偏移
            void WriteBufferAndPush(const Buffer &buffer);
            // 读取数据
            void Read(char *data, uint64_t size);
            // 读取数据并移动读偏移
            void ReadAndPop(char*data,uint64_t size);
            // 字符串形式的读取数据
            std::string ReadString(uint64_t size);
            // 字符串形式的读取数据并移动读偏移
            void ReadStrAndPop(std::string &str,uint64_t size);
            //寻找换行字符
            char* FindCRLF();
            const char* FindCRLF() const;
            //获取一整行的字符
            const char* GetLine() const;
            const char* GetLineAndPop() ;
            //获取一整行的字符串
            std::string GetLineStr() const;
            std::string GetLineStrAndPop() ;
            // 清空缓冲区
            void Clear();
            
    };
}

buffer.cpp

cpp 复制代码
#include "buffer.hpp"

namespace ImMuduo
{
    Buffer::Buffer()
        : buffer_(DefaultBufferSize), read_(0), write_(0)
    {}
    char* Buffer::begin()
    {
        return buffer_.data();
    }
    const char* Buffer::begin() const
    {
        return buffer_.data();
    }
    char* Buffer::WritePos()
    {
        return begin() + write_;
    }
    const char* Buffer::ReadPos() const
    {
        return begin() + read_;
    }
    uint64_t Buffer::FrontFreeSize()
    {
        return read_;
    }
    uint64_t Buffer::BackFreeSize()
    {
        return buffer_.size() - write_;
    }
    uint64_t Buffer::ReadableSize() const
    {
        return write_ - read_;
    }
    void Buffer::MoveReadPos(uint64_t size)
    {
        // 确保读取的字节数不超过可读字节数
        assert(size <= ReadableSize());
        read_ += size;
    }
    void Buffer::MoveWritePos(uint64_t size)
    {
        // 确保写入的字节数不超过可写字节数
        assert(size <= BackFreeSize());
        write_ += size;
    }
    void Buffer::EnsureWriteable(uint64_t size)
    {
        // 确保可写空间足够
        if(BackFreeSize() >=size)
        {
            return ;
        }
        // 确保整体空闲空间足够

        if(size<=FrontFreeSize()+BackFreeSize())
        {
            // 移动数据
            uint64_t moveSize = ReadableSize();
            std::copy(ReadPos(), ReadPos() + moveSize, begin());//可读数据保存到起始位置
            read_ = 0;
            write_ = moveSize;
        }
        else
        {
            //不移动数据,直接在写指针后扩容即可
            buffer_.resize(write_+size);
        }
    }
    void Buffer::Write(const char *data, uint64_t size)
    {
        EnsureWriteable(size);
        std::copy(data, data+size, WritePos());
        write_ += size;
    }
    void Buffer::WriteAndPush(const char *data, uint64_t size)
    {
        Write(data, size);
        MoveWritePos(size);
    }
    void Buffer::WriteString(const std::string &str)
    {
        Write(str.c_str(), str.size());
    }
    void Buffer::WriteStrAndPush(const std::string &str)
    {
        WriteString(str);
        MoveWritePos(str.size());
    }

    void Buffer::WriteBuffer(const Buffer &buffer)
    {
        Write(buffer.ReadPos(), buffer.ReadableSize());
    }
    void Buffer::WriteBufferAndPush(const Buffer &buffer)
    {
        WriteBuffer(buffer);
        MoveWritePos(buffer.ReadableSize());
    }
    void Buffer::Read(char *data, uint64_t size)
    {
        assert(size <= ReadableSize());
        std::copy(ReadPos(), ReadPos() + size, data);
        read_ += size;
    }
    void Buffer::ReadAndPop(char*data,uint64_t size)
    {
        assert(size <= ReadableSize());
        Read(data, size);
        MoveReadPos(size);
    }
    std::string Buffer::ReadString(uint64_t size)
    {
        assert(size <= ReadableSize());
        std::string str;
        str.resize(size);
        Read(str.data(), size);
        return str;
    }
    void Buffer::ReadStrAndPop(std::string &str,uint64_t size)
    {
        assert(size <= ReadableSize());
        str.resize(size);
        Read(str.data(), size);
        MoveReadPos(size);
    }
    char* Buffer::FindCRLF()
    {
        const char crlf[] = {'\r', '\n'};
        const char* start = ReadPos();
        const char* end = start + ReadableSize();
        
        const char* found = std::search(start, end, crlf, crlf + 2);
        
        if (found != end) {
            return const_cast<char*>(found);
        }
        return nullptr;
    }
    const char* Buffer::FindCRLF() const
    {
        const char crlf[] = {'\r', '\n'};
        const char* start = ReadPos();
        const char* end = start + ReadableSize();
        
        const char* found = std::search(start, end, crlf, crlf + 2);
        
        if (found != end) {
            return found;
        }
        return nullptr;
    }
    const char* Buffer::GetLine() const
    {
        const char* crlf = FindCRLF();
        if (crlf != nullptr) {
            return ReadPos();
        }
        return nullptr;
    }
    std::string Buffer::GetLineStr() const
    {
        const char* crlf = FindCRLF();
        if (crlf != nullptr) {
            return std::string(ReadPos(), crlf);
        }
        return "";
    }
    const char* Buffer::GetLineAndPop()
    {
        const char* crlf = FindCRLF();
        if (crlf != nullptr) {
            const char* lineStart = ReadPos();
            uint64_t lineSize = crlf - ReadPos() + 2;
            MoveReadPos(lineSize);
            return lineStart;
        }
        return nullptr;
    }
    std::string Buffer::GetLineStrAndPop()
    {
        const char* crlf = FindCRLF();
        if (crlf != nullptr) {
            std::string line(ReadPos(), crlf);
            uint64_t lineSize = crlf - ReadPos() + 2;
            MoveReadPos(lineSize);
            return line;
        }
        return "";
    }
    void Buffer::Clear()
    {
        read_ = 0;
        write_ = 0;
    }
}

socket模块

对于socket模块,我们要实现以下功能:

创建套接字

绑定地址信息

开始监听

向服务器发起连接

获取新连接

接收数据

发送数据

关闭套接字

创建一个服务端连接

创建一个客户端连接

设置套接字选项---开启地址端口重用

设置套接字阻塞属性---设置为非阻塞

源码

socket.hpp

cpp 复制代码
#include<cstdint>
#include<string>
namespace ImMuduo 
{
    const int kDefaultBacklog = 1024;
    const uint16_t kDefaultPort = 8080;
    class Socket
    {
        int sockfd_;
        public:
        Socket();
        ~Socket();
        Socket(int sockfd);
        // 获取套接字描述符  
        int fd();
        // 创建套接字  
        bool Create();
        // 绑定地址信息  
        bool Bind(const std::string &ip, uint16_t port = kDefaultPort);
        // 开始监听  
        bool Listen(int backlog = kDefaultBacklog);
        // 向服务器发起连接  
        bool Connect(const std::string &ip, uint16_t port = kDefaultPort);
        // 获取新连接  
        bool Accept();
        // 接收数据  
        ssize_t Recv(void *buf, size_t size,int flags);
        // 非阻塞接收数据  
        ssize_t RecvNoBlock(void *buf, size_t size);
        // 发送数据  
        ssize_t Send(const void *buf, size_t len);
        // 关闭套接字  
        bool Close();
        // 创建一个服务端连接  
        bool CreateServer(const std::string &ip, uint16_t port = kDefaultPort);
        // 创建一个客户端连接  
        bool CreateClient(const std::string &ip, uint16_t port = kDefaultPort);    
        // 设置套接字选项---开启地址端口重用  
        void SetReuseAddr();
        // 设置套接字阻塞属性---设置为非阻塞
        void SetNonBlock();
    };
}

socket.cpp

cpp 复制代码
#include "socket.hpp"
#include "Log.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <cerrno>

namespace ImMuduo 
{

    Socket::Socket() : sockfd_(-1) {}

    Socket::~Socket() { Close(); }

    Socket::Socket(int sockfd) : sockfd_(sockfd) {}

    // 获取套接字描述符  
    int Socket::fd() { return sockfd_; }

    // 创建套接字  
    bool Socket::Create() 
    {
        sockfd_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(sockfd_ < 0) 
        {
            ERROR("Failed to create socket: %s", strerror(errno));
            return false;
        }
        INFO("Socket created successfully, fd: %d", sockfd_);
        return true;
    }

    bool Socket::Bind(const std::string &ip, uint16_t port)
    {
        if (sockfd_ < 0) return false;

        struct sockaddr_in addr;
        std::memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if (::inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0)
        {
            ERROR("Failed to convert IP address to binary form: %s", ip.c_str());
            return false;
        }
        int ret = ::bind(sockfd_, reinterpret_cast<struct sockaddr*>(&addr),
                        sizeof(addr));
        if (ret < 0)
        {
            ERROR("Failed to bind socket to address: %s", strerror(errno));
            return false;
        }
        INFO("Socket bound successfully to address: %s:%d", ip.c_str(), port);
        return true;
    }

    bool Socket::Listen(int backlog)
    {
        if (sockfd_ < 0) return false;
        int ret = ::listen(sockfd_, backlog);
        if (ret < 0) 
        {
            ERROR("Failed to listen on socket: %s", strerror(errno));
            return false;
        }
        INFO("Socket is now listening on backlog: %d", backlog);
        return true;
    }

    bool Socket::Connect(const std::string &ip, uint16_t port)
    {
        if (sockfd_ < 0) return false;

        struct sockaddr_in addr;
        std::memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if (::inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0)
        {
            ERROR("Failed to convert IP address to binary form: %s", ip.c_str());
            return false;
        }
        int ret = ::connect(sockfd_, reinterpret_cast<struct sockaddr*>(&addr),
                        sizeof(addr));
        if (ret < 0)
        {
            ERROR("Failed to connect to address: %s", strerror(errno));
            return false;
        }
        INFO("Socket connected successfully to address: %s:%d", ip.c_str(), port);
        return true;
    }

    bool Socket::Accept() 
    {
        if (sockfd_ < 0) return false;

        int newfd = ::accept(sockfd_, nullptr, nullptr);
        if (newfd < 0) 
        {
            ERROR("Socket accept failed: %d", errno);
            return false;
        }

        // 关闭旧的监听 socket,将当前对象转换为连接 socket
        Close();
        sockfd_ = newfd;
        return true;
    }

    ssize_t Socket::Recv(void *buf, size_t size, int flags) 
    {
        if (sockfd_ < 0) return -1;
        ssize_t ret = ::recv(sockfd_, buf, size, flags);
        if(ret <=0) 
        {
            //EAGAIN:当前缓冲区无数据可读,非阻塞才有这两个错误
            //EINTR:被信号中断,需要重新调用系统调用
            if(errno == EAGAIN || errno == EINTR)
            {
                INFO("Reception succeeded but no data was received");//接收成功但是没有接收到数据
                return ret;
            }
            ERROR("Reception failed: %s", strerror(errno));
            return -1;
        }
        return ret;
    }
    ssize_t Socket::RecvNoBlock(void *buf, size_t size)
    {
        return Recv(buf, size, MSG_DONTWAIT);//非阻塞接收数据
    }
    ssize_t Socket::Send(const void *buf, size_t len) 
    {
        if (sockfd_ < 0) return -1;
        ssize_t ret = ::send(sockfd_, buf, len, 0);
        if(ret < 0) 
        {
            ERROR("Failed to Send data: %s", strerror(errno));
            return -1;
        }
        return ret;
    }

    bool Socket::Close() 
    {
        if (sockfd_ >= 0) 
        {
            ::close(sockfd_);
            sockfd_ = -1;
            return true;
        }
        return false;
    }

    bool Socket::CreateServer(const std::string &ip, uint16_t port) 
    {
        //1、创建套接字2、绑定地址3、监听队列4、设置非阻塞5、设置复用地址
        if(Create()==false) return false;
        SetNonBlock(); 
        SetReuseAddr();
        if(Bind(ip, port)==false) return false;
        if(Listen()==false) return false;
        
        
        return true;
    }

    bool Socket::CreateClient(const std::string &ip, uint16_t port) 
    {
        //1、创建套接字2、连接服务器3、设置非阻塞
        if(Create()==false) return false;
        if(Connect(ip, port)==false) return false;
        SetNonBlock();
        return true;
    }

    void Socket::SetReuseAddr() 
    {
        int opt = 1;
        setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//开启地址端口重用
        setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));//开启端口重用
    }

    void Socket::SetNonBlock() {
        int flags = ::fcntl(sockfd_, F_GETFL, 0);
        flags |= O_NONBLOCK;
        ::fcntl(sockfd_, F_SETFL, flags);
    }

}  // namespace ImMuduo

本期内容就到这里了,喜欢请点个赞谢谢

封面图自取:

相关推荐
starvapour8 小时前
INTEL-C621A芯片组服务器主板如何修改pcie等级
运维
酸钠鈀8 小时前
AI M61SDK Ubuntu 环境搭建
linux·运维·ubuntu
2601_949817928 小时前
nginx 代理 redis
运维·redis·nginx
JiaWen技术圈8 小时前
netfiler 协议栈钩子
linux·运维·服务器·安全
橙子也要努力变强8 小时前
进程与信号
linux·服务器·c++
budingxiaomoli8 小时前
服务注册-服务实现
运维·springcloud
java1234_小锋8 小时前
解释一下NGINX的反向代理和正向代理的区别?
运维·nginx
广州服务器托管8 小时前
[2026.4.27]WIN10.1809.17763.8647[PIIS]中简优化版LTSC2019 丝滑流畅 老爷机续命系统
运维·人工智能·windows·计算机网络·可信计算技术
Hommy888 小时前
【开源剪映小助手】视频生成接口
服务器·github·aigc·剪映小助手·视频剪辑自动化