本期我们就这着来深入学习该项目
具体源码实现如下:仿muduo服务器: 本项目致力于实现一个仿造muduo库的简易并发服务器,为个人项目,参考即可
目录
buffer缓冲区模块
提供的功能:存储数据,取出数据实现思想:
- 实现缓冲区有一块内存空间,采用 vector<char>vector 底层其实使用的就是一个线性的内存空间
- 要素:
- 默认的空间大小;
- 当前的读取数据位置
- 当前的写入数据位置
操作:
-
写入数据:当前写入位置指向哪里,就从哪里开始写入。如果后续剩余空闲空间不够了就扩容
-
考虑整体缓冲区空闲空间是否足够(因为该位置也会向后偏移,前边有可能会有空闲空间)
足够 :将数据移动到起始位置即可
不够 :扩容,从当前写位置开始扩容足够大小
数据一旦写入成功,当前写位置,就要向后偏移
- 读取数据:
当前的读取位置指向哪里,就从哪里开始读取,前提是有数据可读
可读数据大小:当前写入位置,减去当前读取位置
源码
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
本期内容就到这里了,喜欢请点个赞谢谢
封面图自取:
