004 仿muduo實現高性能服務器組件_Buffer模塊與Socket模塊的實現

?🌈個人主頁:Fan_558
🔥 系列專欄:仿muduo
🌹關注我💪🏻帶你學更多知識

文章目錄

  • 前言
    • Buffer模塊
    • Socket模塊
  • 小結

前言

這章將會向你介紹仿muduo高性能服務器組件的buffer模塊與socket模塊的實現

Buffer模塊

在這里插入圖片描述
設計思想
在這里插入圖片描述
實現思想:

1、實現緩沖區得有一塊內存空間,采用vector,string字符串的操作遇到’\0’就停止了,網絡操作中什么樣的數據都有,'\0’可能也有,string大部分的操作都是字符串操作,所以不太行

2、記錄當前的讀取數據位置與當前的寫入數據位置,避免每次寫入數據需要重新遍歷數組找寫入讀入位置

3、考慮整體緩沖區空閑空間是否足夠 (因為讀位置也會向后偏移,前邊有可能會有空間) 足夠:則將數據(讀位置開始)移動到起始位置即可
不夠:擴容,從當前寫位置開始擴容足夠大小 數據一旦寫入成功,當前寫位置就要向后偏移

4、讀取數據/寫入數據
當前的讀取/寫入位置指向哪里,就從哪里開始讀取/寫入,前提是有數據可讀/有空間可寫,讀取/寫入完數據,讀偏移/寫偏移向后偏移

為了方便查閱
在這里插入圖片描述
代碼如下:

class Buffer{
private:std::vector<char> _buffer; //使用vector進行內存空間管理uint64_t _reader_idx; //讀偏移uint64_t _writer_idx; //寫偏移
public:Buffer():_reader_idx(0), _writer_idx(0) ,_buffer(BUFFER_SIZE) {}//獲取_buffer起始元素的地址char* begin() {return &*_buffer.begin();}//獲取當前寫入起始地址(_buffer的空間起始地址,加上寫偏移量char* WritePos() { return begin() + _writer_idx; }//獲取當前讀取起始地址(_buffer的空間起始地址,加上讀偏移量char* ReadPos() { return begin() + _reader_idx; }//獲取緩沖區末尾空閑空閑大小--寫偏移之后的空閑空間uint64_t TailIdleSize() {return _buffer.size() - _writer_idx; }//獲取緩沖區起始地址空閑空間大小--讀偏移之前的空閑空間uint64_t HeadIdleSize() {return _reader_idx; }//獲取可讀數據大小uint64_t ReadAbleSize() {return _writer_idx - _reader_idx; }//讀取數據后,將讀偏移向后移動void MoveReadOffest(uint64_t len) {   //向后移動的大小,必須小于可讀數據大小assert(len <= ReadAbleSize());_reader_idx += len; }//寫入數據后,將寫偏移向后移動void MoveWriteOffest(uint64_t len) { _writer_idx += len; }//確保可寫空間足夠(整體空閑空間夠了就移動數據,否則就擴容)void EnsureWriteSpace(uint64_t len){//如果末尾空閑空間大小足夠,直接返回if(len < TailIdleSize()) return;//如果不夠,判斷加上起始位置的空閑空間大小是否足夠,夠了就將可讀數據移動到起始位置else if(len <= HeadIdleSize() + TailIdleSize()) {uint64_t sz = ReadAbleSize();   //可讀數據大小_reader_idx = 0;    //更新讀偏移_writer_idx = sz;   //更新寫偏移return;}//總體空間不夠,則需要擴容,不移動數據,直接給寫偏移之后擴容足夠空間即可else _buffer.resize(_writer_idx + len);}//寫入數據void Write(const void* data, uint64_t len){//保證是否有足夠空間EnsureWriteSpace(len);const char* d = (const char* )data;//拷貝數據到buffer當中std::copy(d, d + len, WritePos());}void WriteAndPush(const void* data, uint64_t len){Write(data, len);MoveWriteOffest(len);}//寫入一個字符串void WriteString(const std::string &data){Write(data.c_str(), data.size());}//向buffer中寫入一個字符串并向后移動writevoid WriteStringAndPush(const std::string &data){WriteString(data);MoveWriteOffest(data.size());}//把一個buffer類型的數據寫入void WriteBuffer(Buffer &data){Write(data.ReadPos(), data.ReadAbleSize());}//向buffer中寫入一個并向后移動writevoid WriteBufferAndPush(Buffer &data){WriteBuffer(data);MoveWriteOffest(data.ReadAbleSize());}//讀取數據void Read(void* buf, uint64_t len){assert(len <= ReadAbleSize());//保持參數類型一致std::copy(ReadPos(), ReadPos() + len, (char*)buf);}void ReadAndPop(void* buf, uint64_t len){Read(buf, len);MoveReadOffest(len);}//把讀取的數據當作一個string返回  std::string ReadAsString (uint64_t len){assert(len <= ReadAbleSize());std::string str;str.resize(len);//從緩沖區中讀取長度為len的數據,并將其存儲到字符串str的內存地址開始處的位置Read(&str[0], len);return str;}//讀取一個string并向后移動(確保下一次不會重復讀取)std::string ReadAsStringAndPop(uint64_t len){assert(len <= ReadAbleSize());std::string str = ReadAsString(len);MoveReadOffest(len);return str;}/*由于后面我們的高并發服務器會支持應用層協議的HTTP,而在HTTP協議中通常就是讀取一行的數據,因為請求行和請求報頭以及響應行和響應報頭都是以\r\n作為分隔符的,都是一行行的數據所以我們的緩沖區也提供一個查找換行字符的位置*/char* FindCRLF(){//在可讀數據范圍內查找第一個出現的換行符的位置char* res = (char*)memchr(ReadPos(), '\n', ReadAbleSize());return res;}//獲取一行數據std::string Getline(){char* pos = FindCRLF();if(pos == nullptr) return "";/*將換行符\n前的數據讀出,+1:包括換行符(不然的話下一次再查找,換行符就在開頭) */return ReadAsString(pos - ReadPos() + 1); }//讀出一行數據后,將讀偏移向后移std::string GetLineAndPop(){std::string str = Getline();MoveReadOffest(str.size());return str;}//清空緩沖區void clear(){//只需要將偏移量歸零_writer_idx =  _reader_idx = 0;}
};

Socket模塊

設計思想:
在這里插入圖片描述
在該模塊當中除了對socket套接字原有的操作進行封裝,還提供了直接創建服務端和客戶端連接的接口
為了方便查閱
在這里插入圖片描述

代碼如下

#define MAX_LISTEN 1024
class Socket{private:int _sockfd;public:Socket():_sockfd(-1){}Socket(int fd):_sockfd(fd){}//關閉套接字~Socket() { Close(); }int Fd(){return _sockfd;}//創建套接字bool Create(){//int socket(int domain, int type, int protocol)  AF_INET: 表示使用ipv4地址族 SOCK_STREM: 表示創建面向連接的套接字類(TCP) IPPROTO_TCP: 表示使用TCP協議_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(_sockfd < 0){ERR_LOG("CREATE SOCKET FAILEDQ!");return false;}return true;}//綁定地址信息bool Bind(const std::string &ip, uint16_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;      //ipv4地址域類型addr.sin_port = htons(port);    //將端口號通過主機轉網絡字節序addr.sin_addr.s_addr = inet_addr(ip.c_str());   //將IP地址轉化為網絡字節序的32位ipv4地址socklen_t len = sizeof(struct sockaddr_in);//int bind(int socket, const struct sockaddr *addr. socklen_t addrlen);int ret = bind(_sockfd, (struct sockaddr*)&addr, len);if(ret < 0){ERR_LOG("BIND ADDRESS FAILEDQ!");return false;}return true;}//開始監聽bool Listen(int backlog = MAX_LISTEN){int ret = listen(_sockfd, backlog);if(ret < 0){ERR_LOG("SOCKET LISTEN FAILED!");return false;}return true;}//向服務器發起連接(傳入服務器的ip和端口信息)bool Connect(const std::string &ip, uint16_t port){//int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(struct sockaddr_in);int ret = connect(_sockfd, (struct sockaddr*)&addr, len);if(ret < 0){ERR_LOG("CONNECT SERVER FAILEDQ!");return false;}return true;}//監聽有新連接后,獲取新連接(返回一個文件描述符)int Accept()    {int newfd = accept(_sockfd, nullptr, nullptr);if(newfd < 0){ERR_LOG("SOCKET ACCEPT FAILED!");return -1;}return newfd;}//接收數據(ssize_t為有符號整數,size_t無符號整數,默認0為阻塞操作)ssize_t Recv(void* buf, size_t len, int flag = 0){ssize_t ret = recv(_sockfd, buf, len, flag);if(ret <= 0){//EAGAIN 當前socket的接收緩沖區中沒有數據了,在非阻塞的情況下才會有這個錯誤//EINTR 當前socket的阻塞等待被信號打斷了if(errno == EAGAIN || errno == EINTR)return 0;else{ERR_LOG("SOCKET RECV FAILED");return -1;}}return ret; //返回實際接收的數據長度}ssize_t NonBlockRecv(void* buf, size_t len){return Recv(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示當前接收為非阻塞}//發送數據ssize_t Send(const void* buf, size_t len, int flag = 0){ssize_t ret = send(_sockfd, buf, len, flag);if(ret < 0){if(errno == EAGAIN || errno == EINTR){return 0;}ERR_LOG("SOCKET RECV FAILED");return -1;}return ret; //返回實際發送的數據長度}ssize_t NonBlockSend(void* buf, size_t len){Send(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示當前接收為非阻塞}//關閉套接字void Close(){if(_sockfd != -1){close(_sockfd);_sockfd = -1;}}//創建一個服務端連接bool CreateServer(uint16_t port, const std::string &ip = "0.0.0.0", bool block_flag = false){if(Create()==false) return false;//是否啟動非阻塞if(block_flag) NonBlock();if(Bind(ip, port) == false) return false;if(Listen() == false) return false;ReuseAddress();return true;}//創建一個客戶端連接bool CreateClient(uint16_t port, const std::string &ip){if(Create() == false) return false;if(Connect(ip, port) == false) return false;return true;}//設置套接字選項---開啟地址端口重用void ReuseAddress(){// int setsockopt(int fd, int leve, int optname, void *val, int vallen)int val = 1;setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));val = 1;setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void*)&val, sizeof(int));}//設置套接字阻塞屬性---設置為非阻塞void NonBlock(){//int fcntl(int fd, int cmd, ... /* arg */ );int flag = fcntl(_sockfd, F_GETFL, 0);fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);}
};

小結

今日的項目分享就到這里啦,下一篇將會向你介紹Channel與Poller模塊

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20123.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20123.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20123.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Leetcode 706 】設計哈希映射——數組嵌套鏈表(限制哈希Key)

題目 不使用任何內建的哈希表庫設計一個哈希映射&#xff08;HashMap&#xff09;。 實現 MyHashMap 類&#xff1a; MyHashMap() 用空映射初始化對象void put(int key, int value) 向 HashMap 插入一個鍵值對 (key, value) 。如果 key 已經存在于映射中&#xff0c;則更新其…

MATLAB的plot3使用技巧|更改視角|例程分享鏈接

plot3命令 MATLAB的plot3函數是用來繪制3D圖形的函數。它可以將三維數據可視化為線段、點、曲線等形式。plot3函數可以用于繪制三維空間中的曲線、曲面、散點圖等。 plot3函數的基本用法是&#xff1a; plot3(X,Y,Z)&#xff1a;繪制三維線段&#xff0c;其中X、Y、Z分別是包…

兩個雙指針 的 “他“和“ 她“會相遇么? —— “雙指針“算法 (Java版)

本篇會加入個人的所謂魚式瘋言 ??????魚式瘋言:??????此瘋言非彼瘋言 而是理解過并總結出來通俗易懂的大白話, 小編會盡可能的在每個概念后插入魚式瘋言,幫助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能說的不是那么嚴謹.但小編初心是能讓更多人能接…

MySQL入門學習-查詢進階.UNION

UNION操作符用于合并兩個或多個SELECT語句的結果集。它可以將多個查詢結果合并為一個結果集&#xff0c;這在需要從多個表中獲取數據并將它們組合在一起時非常有用。下面是一個使用UNION的示例代碼&#xff1a; SELECT column1, column2,...FROM table1UNIONSELECT column1, c…

springboot kafka 提高拉取數量

文章目錄 背景問題復現解決問題原理分析fetch.min.bytesfetch.max.wait.ms源碼分析ReplicaManager#fetchMessages 背景 開發過程中&#xff0c;使用kafka批量消費&#xff0c;發現拉取數量一直為1&#xff0c;如何提高批量拉取數量&#xff0c;記錄下踩坑記錄。 問題復現 ka…

攻防對抗少丟分,愛加密幫您筑起第二防線

應用程序通常處理和存儲大量的敏感數據&#xff0c;如用戶個人信息、財務信息、商業數據、國家數據等&#xff0c;用戶量越大的應用程序&#xff0c;其需要存儲和保護的用戶數據越多。因此應用層長期是攻擊方的核心目標&#xff0c;傳統應用安全依靠防火墻(FireWall)、入侵檢測…

1.7 協議層次和服務模型

協議層次 網絡是一個復雜的系統! ? 網絡功能繁雜&#xff1a;數字信號的物理信 號承載、點到點、路由、rdt、進程區分、應用等 ?現實來看&#xff0c;網絡的許多構成元素和設備: ? 主機 ? 路由器 ? 各種媒體的鏈路 ? 應用 ? 協議 ? 硬件, 軟件 Q:如何組織和實現這個…

Linux上實現ssh免密通訊

Linux上實現ssh免密通訊 1.SSH互信原理2.SSH所需的RPM包3.兩臺機器實現互信4.常見問題及處理 1.SSH互信原理 SSH&#xff08;Secure Shell&#xff09;是一種安全的傳輸協議&#xff0c;它能讓Linux系統中的服務器和客戶端之間進行安全可靠的通訊。 SSH使用加密的傳輸方式&…

iOS組件化 方案 實現

iOS組件化 組件化的原因現在流行的組件化方案方案一、url-block &#xff08;基于 URL Router&#xff09;方案二、protocol調用方式解讀 方案三、target-action調用方式解讀 gitHub代碼鏈接參考 組件化的原因 模塊間解耦模塊重用提高團隊協作開發效率單元測試 當項目App處于…

網絡原理-四

一、續 當窗口大小為0,意味著緩沖區滿了,此時發送方,就因該暫停發送,發送方會周期性的除法 " 窗口探測包 " ,并不攜帶載荷,這樣的包對于業務不產生影響,只是為了觸發ACK,一旦查詢出來的結果是非0,緩沖區右有空間了,發送方就可以繼續發送. 二、擁塞控制 要限制發送方…

一步一步寫線程之十三隊列間的消息通知

一、線程和分布式的通信 隨著技術的不斷發展&#xff0c;多線程和分布式通信愈發的普及。那么在這種場景下的如何進行數據的通信&#xff0c;便成為了一個非常典型的問題。無論是多線程還是分布式&#xff0c;其實其抽象出來的通信機制都是類似的。或者說換句話&#xff0c;多…

java檢測字符串是否包含數字和字母

在Java中&#xff0c;要檢測一個字符串是否同時包含數字和字母&#xff0c;我們可以使用正則表達式&#xff08;regex&#xff09;或者通過遍歷字符串并檢查每個字符來實現。以下是兩種方法的詳細代碼示例&#xff1a; 1.方法一&#xff1a;使用正則表達式 import java.util.…

【AI+知識庫問答】沉浸式體驗了解 AI知識庫問答fastGPT

之前寫過一篇文章 【AI本地知識庫】個人整理的幾種常見本地知識庫技術方案 &#xff0c; 由于當時主要是針對AI本地知識庫&#xff0c; 所以沒列fastGPT。 最近經常刷到fastGPT&#xff0c;這里單獨水一篇。 FastGPT 是一個基于 LLM 大語言模型的知識庫問答系統&#xff0c;…

Github 2024-06-01 開源項目日報Top10

根據Github Trendings的統計,今日(2024-06-01統計)共有10個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Python項目5Jupyter Notebook項目2TypeScript項目1Go項目1Shell項目1Lua項目1Kong:云原生API網關與AI能力 創建周期:3482 天開發語言:Lua協議…

如何確保績效目標執行到位?

很多企業在實施績效過程中&#xff0c;盡管制定好了績效目標&#xff0c;但是沒有執行下去&#xff0c;管理者將原因歸咎于“員工低效”、“體制機制”等問題&#xff0c;那么在人力資源管理方面&#xff0c;企業應該如何確保制定的績效目標執行到位&#xff1f;如何提高低效能…

云原生架構相關技術_4.服務網格

1.技術特點 服務網格&#xff08;ServiceMesh&#xff09;是分布式應用在微服務軟件架構之上發展起來的新技術&#xff0c;旨在將那些微服務間的連接、安全、流量控制和可觀測等通用功能下沉為平臺基礎設施&#xff0c;實現應用與平臺基礎設施的解耦。這個解耦意味著開發者無需…

React@16.x(14)context 舉例 - Form 表單

目錄 1&#xff0c;目標2&#xff0c;實現2.1&#xff0c;index.js2.2&#xff0c;context.js2.2&#xff0c;Form.Input2.3&#xff0c;Form.Button 3&#xff0c;使用 1&#xff0c;目標 上篇文章說到&#xff0c;context 上下文一般用于第3方組件庫&#xff0c;因為使用場景…

Chisel入門——在windows下vscode搭建|部署Scala2.13.3開發環境|用Chisel點亮FPGA小燈等實驗

文章目錄 前言一、vscode搭建scala開發環境1.1 安裝Scala官方插件1.2 創建hello_world.scala文件1.3 確認java的版本(博主使用的是1.8)1.4 下載Scala Windows版本的二進制文件1.5 配置環境變量1.6 交互模式測試一下1.7 vscode運行scala 二、windows安裝sbt2.1 下載sbt2.2 設置環…

函數遞歸及具體例子(持續更新)

遞歸就是函數自己調用自己 求n的階乘 n! n * (n - 1)! 直到n為1或者0的時候為止 舉個例子 int Fun(int n) {if (n < 0){return 1;}else{return n * Fun(n - 1);} }int main() {int n 0;scanf("%d", &n);int ret Fun(n);printf("%d\n", ret…

安裝Kubernetes v3 ----以docker的方式部署

以docker的方式部署 docker run -d \ --restartunless-stopped \ --namekuboard \ -p 80:80/tcp \ -p 10081:10081/tcp \ -e KUBOARD_ENDPOINT"http://192.168.136.55:80" \ -e KUBOARD_AGENT_SERVER_TCP_PORT"10081" \ -v /root/kuboard-data:/data \ e…