【Linux網絡】應用層自定義協議與序列化及Socket模擬封裝

📢博客主頁:https://blog.csdn.net/2301_779549673
📢博客倉庫:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正!
📢本文由 JohnKi 原創,首發于 CSDN🙉
📢未來很長,值得我們全力奔赴更美好的生活?

在這里插入圖片描述

在這里插入圖片描述

文章目錄

  • 🏳??🌈一、應用層
    • 1.1 再談 "協議"
    • 1.2 網絡版計算器
    • 1.3 序列化和反序列化
  • 🏳??🌈二、什么是全雙工
  • 🏳??🌈三、Socket封裝
    • 3.1 整體結構
    • 3.2 Socket 基類
    • 3.3 TcpSocket 子類
      • 3.3.1 基本結構
      • 3.3.2 構造、析構函數
      • 3.3.3 創建套接字
      • 3.3.4 綁定套接字
      • 3.3.5 監聽套接字
      • 3.3.6 獲取連接
      • 3.3.7 建立連接
      • 3.3.8 其他方法
      • 3.3.10 整體代碼
  • 👥總結


11111111
11111111
11111111
11111111
**** 11111111

🏳??🌈一、應用層

我們程序員寫的一個個解決我們實際問題, 滿足我們日常需求的網絡程序, 都是在應用層.

1.1 再談 “協議”

協議是一種 “約定”. socket api 的接口, 在讀寫數據時, 都是按 “字符串” 的方式來發送接收的. 如果我們要傳輸一些 “結構化的數據” 怎么辦呢?

其實,協議就是雙方約定好的結構化的數據!

1.2 網絡版計算器

例如, 我們需要實現一個服務器版的加法器. 我們需要客戶端把要計算的兩個加數發過去, 然后由服務器進行計算, 最后再把結果返回給客戶端.

約定方案一(傳結構體對象):

  • 客戶端發送一個形如"1+1"的字符串;
  • 這個字符串中有兩個操作數, 都是整形;
  • 兩個數字之間會有一個字符是運算符, 運算符只能是 + ;
  • 數字和運算符之間沒有空格;

不推薦直接傳結構體對象,從技術和業務角度解釋?

1、技術角度

  • 1、跨平臺與兼容性:

    • 結構體的大小和內存布局可能因編譯器、操作系統或硬件平臺的不同而有所差異。這可能導致在一個平臺上發送的結構體在另一個平臺上無法正確解析。
  • 2、內存對齊與填充:

    • 為了優化內存訪問速度,編譯器可能會對結構體成員進行對齊和填充。這會導致結構體的實際大小大于其成員大小的總和。
    • 直接傳輸結構體可能會因為內存對齊和填充的問題而導致數據解析錯誤。
  • 3、指針與動態內存:

    • 結構體中可能包含指針,這些指針指向動態分配的內存。直接傳輸結構體無法傳遞指針所指向的數據,而只能傳遞指針值,這可能導致數據丟失或內存泄漏。

2、業務角度

  • 1、數據安全性:
    • 直接傳輸結構體可能會暴露數據的內部結構和實現細節,從而增加數據被惡意攻擊的風險。
  • 2、數據版本控制:
    • 隨著業務的發展和變化,數據結構和格式可能需要進行調整和升級。

約定方案二(傳字符串):

  • 定義結構體來表示我們需要交互的信息;
  • 發送數據時將這個結構體按照一個規則轉換成字符串, 接收到數據的時候再按照相同的規則把字符串轉化回結構體;
  • 這個過程叫做 “序列化” 和 “反序列化

1.3 序列化和反序列化

在這里插入圖片描述

無論我們采用方案一, 還是方案二, 還是其他的方案, 只要保證, 一端發送時構造的數據,在另一端能夠正確的進行解析, 就是 ok 的. 這種約定, 就是 應用層協議

但是,為了讓我們深刻理解協議,我們打算自定義實現一下協議的過程。

  • 我們采用方案 2,我們也要體現協議定制的細節
  • 我們要引入序列化和反序列化,只不過我們課堂直接采用現成的方案 – jsoncpp庫
  • 我們要對 socket 進行字節流的讀取處理

🏳??🌈二、什么是全雙工

在這里插入圖片描述

所以:

  • 在任何一臺主機上,TCP 連接既有發送緩沖區,又有接受緩沖區,所以,在內核中,可以在發消息的同時,也可以收消息,即全雙工
  • 這就是為什么一個 tcp sockfd 讀寫都是它的原因
  • 實際數據什么時候發,發多少,出錯了怎么辦,由 TCP 控制,所以 TCP 叫做傳輸控制協議

1、read,write,send,recv本質是拷貝函數
2、發送數據的本質:是從發送方的發送緩沖區把數據通過協議棧和網絡拷貝給接收方大的接收緩沖區!
3、tcp支持全雙工通信的原因(有發送和接收緩沖區)!
4、有兩個緩沖區這種模式就是生產者消費者模型
5、為什么IO函數要阻塞?本質是在維護同步關系!

TCP協議是面向字節流的,客戶端發的,不一定是全部是服務端收的,怎么保證讀到的是一個完整的請求呢?

  • 分割完整的報文!

🏳??🌈三、Socket封裝

Socket類以模板方法類的設計模式進行封裝,將算法的不變部分封裝在抽象基類中而將可變部分延遲到子類中實現!

3.1 整體結構

  • 抽象類(Abstract Class)

    1. 定義了多個抽象操作,這些操作在抽象類中不具體實現,由子類實現。
    2. 定義了兩個模板方法,這個方法通常調用了上面提到的抽象操作。模板方法的算法骨架是固定的,但其中一些步驟的具體實現會延遲到子類中。
  • 具體子類(Concrete Class)

    1. 實現抽象類中的抽象操作,提供具體的算法步驟實現。
    2. 可以重寫父類中的模板方法,但通常情況下不需要這么做,因為模板方法已經在抽象類中定義好了算法的骨架。

3.2 Socket 基類

將套接字創建、綁定、監聽等 ?通用流程? 抽象為模板方法,如 BuildListenSocket而將具體步驟(如 CreaterSocketOrDie)延遲到子類實現。

?角色劃分?:

  • ?抽象類(Abstract Class)??:Socket 定義了純虛函數(步驟方法)和模板方法(流程框架)。?
  • 具體子類(Concrete Class)??:由用戶繼承 Socket 并實現純虛函數(例如 TcpSocket、UdpSocket)。
using SockPtr = std::shared_ptr<Socket>;class Socket {public:virtual void CreateSocketOrDie() = 0;                 // 創建套接字virtual void BindOrDie(uint16_t port) = 0;            // 綁定套接字virtual void ListenOrDie(int backlog = gbacklog) = 0; // 監聽套接字virtual SockPtr Accepter(InetAddr* cli) = 0;          // 獲取鏈接virtual bool Connector(const std::string& peerip,uint16_t peerport) = 0; // 簡歷連接virtual int Sockfd() = 0;virtual void Close() = 0;virtual ssize_t Recv(std::string* out) = 0;      // 接收數據virtual ssize_t Send(const std::string& in) = 0; // 發送數據public:// 創建監聽套接字void BuildListenSocket(uint16_t port) {CreateSocketOrDie(); // 創建BindOrDie(port);     // 綁定ListenOrDie();       // 監聽}// 創建客戶端套接字void BuildConnectorSocket(const std::string& peerip, uint16_t peerport) {CreateSocketOrDie();         // 創建Connector(peerip, peerport); // 連接}
};

3.3 TcpSocket 子類

TcpSocket類 繼承 Socket類,并具體實現父類的抽象操作!

3.3.1 基本結構

TcpSocket 子類就是具體實現父類的抽象操作,所以所有 TCP 可能會用到的父類方法都要具體實現

class TcpSocket : public Socket {
public:TcpSocket() {}TcpSocket(int sockfd) {}// 創建套接字void CreateSocketOrDie() {}// 綁定套接字void BindOrDie(uint16_t port) {}// 監聽套接字void ListenOrDie(int backlog = gbacklog) {}// 獲取鏈接SockPtr Accepter(InetAddr* cli) {}// 建立連接bool Connector(const std::string& peerip, uint16_t peerport) {}// 獲取套接字描述符int Sockfd() {}// 關閉套接字void Close() {}~TcpSocket() {}private:int _sockfd;
};

3.3.2 構造、析構函數

  • 構造函數 可以實現兩個,一個無參構造,一個有參構造(傳參sockfd),用于初始化成員變量
  • 析構函數 可以不做處理,后面關閉套接字自己調用關閉函數即可!
TcpSocket() {}
TcpSocket(int sockfd) : _sockfd(sockfd) {}
~TcpSocket() {}

3.3.3 創建套接字

使用庫函數 socket 按照格式創建套接字

// 創建套接字
void CreateSocketOrDie() override {_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0) {LOG(LogLevel::ERROR) << "create socket error";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd create success : " << _sockfd;
}

3.3.4 綁定套接字

這部分用于服務端,所以需要先構建服務端的網絡字節序地址,然后綁定套接字和網絡字節序地址

// 綁定套接字void BindOrDie(uint16_t port) override{// sockaddr_in 的頭文件是 #include <netinet/in.h>struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = htonl(INADDR_ANY);int n = ::bind(_sockfd, CONV(&local), sizeof(local));if(n < 0){LOG(LogLevel::ERROR) << "bind socket error";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success";}

3.3.5 監聽套接字

在綁定好網絡字節序地址后,我們需要形成老板模式,設置最大連接數量,然后不斷監聽

// 監聽套接字
void ListenOrDie(int backlog = gbacklog) override {int n = ::listen(_sockfd, backlog);if (n < 0) {LOG(LogLevel::ERROR) << "listen socket error";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen success";
}

3.3.6 獲取連接

在為監聽套接字設置好最大連接長度后,我們不斷使用 accept 監聽這個套接字,將獲取到的客戶端ip和端口號等信息 與 我們的服務端的連接 整合起來,形成一個整體套接字,實現面向對象連接

// 獲取鏈接
SockPtr Accepter(InetAddr* cli) override {struct sockaddr_in client;socklen_t clientlen = sizeof(client);// accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)// 返回一個新的套接字,該套接字與調用進程間接地建立了連接。int sockfd = ::accept(_sockfd, CONV(&client), &clientlen);if (sockfd < 0) {LOG(LogLevel::ERROR) << "accept socket error";return nullptr;}*cli = InetAddr(client);LOG(LogLevel::DEBUG) << "get a new connection from "<< cli->AddrStr().c_str() << ", sockfd : " << sockfd;return std::make_shared<TcpSocket>(sockfd);
}

3.3.7 建立連接

當客戶端想要連接上服務端的時候,我們需要先為服務端創建一個網絡字節序地址,再與客戶端的套接字連接起來

// 建立連接
bool Connector(const std::string& serverip, uint16_t serverport) override {struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;         // IPv4協議server.sin_port = htons(serverport); // 端口號// 這句話表示將字符串形式的IP地址轉換為網絡字節序的IP地址// inet_pton函數的作用是將點分十進制的IP地址轉換為網絡字節序的IP地址// 這里的AF_INET表示IPv4協議// 這里的serverip.c_str()表示IP地址的字符串形式// &server.sin_addr表示將IP地址存儲到sin_addr成員變量中::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // IP地址int n = ::connect(_sockfd, CONV(&server), sizeof(server));if (n < 0) {LOG(LogLevel::ERROR) << "connect socket error";return false;}LOG(LogLevel::DEBUG) << "connect success";return true;
}

3.3.8 其他方法

還有一些其他的方法,難度不大,就不一一介紹了

// 獲取套接字描述符
int Sockfd() override { return _sockfd; }// 關閉套接字
void Close() override {if (_sockfd >= 0)::close(_sockfd);
}// 接收數據
ssize_t Recv(std::string* out) override {char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if (n > 0) {inbuffer[n] = 0;*out = inbuffer;}return n;
}// 發送數據
ssize_t Send(const std::string& in) override {return ::send(_sockfd, in.c_str(), in.size(), 0);
}

3.3.10 整體代碼

#pragma once #include <iostream>
#include <cstring>
#include <Socket.h>
#include <memory>#include <netinet/in.h>#include "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"using namespace LogModule;const int gbacklog = 8;// common.hpp
// #define Die(code)   \
//     do              \
//     {               \
//         exit(code); \
//     } while (0)// #define CONV(v) (struct sockaddr *)(v)// enum 
// {
//     USAGE_ERR = 1,
//     SOCKET_ERR,
//     BIND_ERR,
//     LISTEN_ERR,
//     CONNECTION_ERR
// };namespace SocketModule{using SockPtr = std::shared_ptr<Socket>;class Socket{public:virtual void CreateSocketOrDie() = 0;                                       // 創建套接字virtual void BindOrDie(uint16_t port) = 0;                                  // 綁定套接字virtual void ListenOrDie(int backlog = gbacklog) = 0;                       // 監聽套接字virtual SockPtr Accepter(InetAddr* cli) = 0;                                // 獲取鏈接virtual bool Connector(const std::string& serverip, uint16_t serverport) = 0;   // 簡歷連接virtual int Sockfd() = 0;virtual void Close() = 0;virtual ssize_t Recv(std::string* out) = 0;                                  // 接收數據virtual ssize_t Send(const std::string& in) = 0;                             // 發送數據 public:// 創建監聽套接字void BuildListenSocket(uint16_t port){CreateSocketOrDie();        // 創建BindOrDie(port);            // 綁定ListenOrDie();              // 監聽}// 創建客戶端套接字void BuildConnectorSocket(const std::string& serverip, uint16_t serverport){CreateSocketOrDie();        // 創建Connector(serverip, serverport); // 連接}};class TcpSocket : public Socket{public:TcpSocket(){}TcpSocket(int sockfd) : _sockfd(sockfd){ }// 創建套接字void CreateSocketOrDie() override{_sockfd = socket(AF_INET, SOCK_STREAM, 0);if(_sockfd < 0){LOG(LogLevel::ERROR) << "create socket error";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd create success : " << _sockfd;}// 綁定套接字void BindOrDie(uint16_t port) override{// sockaddr_in 的頭文件是 #include <netinet/in.h>struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = htonl(INADDR_ANY);int n = ::bind(_sockfd, CONV(&local), sizeof(local));if(n < 0){LOG(LogLevel::ERROR) << "bind socket error";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success";}                                  // 監聽套接字void ListenOrDie(int backlog = gbacklog) override{int n = ::listen(_sockfd, backlog);if(n < 0){LOG(LogLevel::ERROR) << "listen socket error";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen success";}// 獲取鏈接SockPtr Accepter(InetAddr* cli) override{struct sockaddr_in client;socklen_t clientlen = sizeof(client);// accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)// 返回一個新的套接字,該套接字與調用進程間接地建立了連接。int sockfd = ::accept(_sockfd, CONV(&client), &clientlen);if(sockfd < 0){LOG(LogLevel::ERROR) << "accept socket error";return nullptr;}*cli = InetAddr(client);LOG(LogLevel::DEBUG) << "get a new connection from " << cli->AddrStr().c_str() << ", sockfd : " << sockfd;return std::make_shared<TcpSocket>(sockfd);}// 建立連接bool Connector(const std::string& serverip, uint16_t serverport) override{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;    // IPv4協議server.sin_port = htons(serverport); // 端口號// 這句話表示將字符串形式的IP地址轉換為網絡字節序的IP地址// inet_pton函數的作用是將點分十進制的IP地址轉換為網絡字節序的IP地址// 這里的AF_INET表示IPv4協議// 這里的serverip.c_str()表示IP地址的字符串形式// &server.sin_addr表示將IP地址存儲到sin_addr成員變量中::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr);   // IP地址int n = ::connect(_sockfd, CONV(&server), sizeof(server));if(n < 0){LOG(LogLevel::ERROR) << "connect socket error" ;return false;}LOG(LogLevel::DEBUG) << "connect success";return true;}// 獲取套接字描述符int Sockfd() override{ return _sockfd; }// 關閉套接字void Close() override{ if(_sockfd >= 0) ::close(_sockfd); }// 接收數據ssize_t Recv(std::string* out) override{char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if(n > 0){inbuffer[n] = 0;*out = inbuffer;}return n;}           // 發送數據                        ssize_t Send(const std::string& in) override{return ::send(_sockfd, in.c_str(), in.size(), 0);}~TcpSocket(){}private:int _sockfd;};
}

👥總結

本篇博文對 【Linux網絡】應用層自定義協議與序列化及Socket模擬封裝 做了一個較為詳細的介紹,不知道對你有沒有幫助呢

覺得博主寫得還不錯的三連支持下吧!會繼續努力的~

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

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

相關文章

基于大模型的結腸癌全病程預測與診療方案研究

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 二、結腸癌概述 2.1 流行病學特征 2.2 發病機制與危險因素 2.3 臨床癥狀與診斷方法 三、大模型技術原理與應用現狀 3.1 大模型的基本原理 3.2 在醫療領域的應用情況 3.3 在結腸癌預測中的潛力分析 四、術前…

【UML建模】starUML工具

一.概述 StarUML是一款UML工具&#xff0c;允許用戶創建和管理UML&#xff08;統一建模語言&#xff09;模型&#xff0c;廣泛應用于軟件工程領域。它的主要功能包括創建各種UML圖&#xff1a;如用例圖、類圖、序列圖等&#xff0c;支持代碼生成與反向工程&#xff0c;以及提供…

模板元編程(Template Metaprogramming, TMP)

C 模板元編程&#xff08;Template Metaprogramming, TMP&#xff09; 模板元編程是一種利用 C 模板系統在 編譯期間 完成計算、類型操作和代碼生成的編程范式。其核心優勢在于通過 零運行時開銷 實現高效、類型安全的代碼。以下是模板元編程的詳細分步解析。 1. 編譯時計算 …

Android Build Variants(構建變體)詳解

Android Build Variants&#xff08;構建變體&#xff09;是 Android 開發中用于生成不同版本應用程序的一種機制。它允許開發者根據不同的需求&#xff0c;如不同的應用市場、不同的功能模塊、不同的環境配置等&#xff0c;從同一個代碼庫中生成多個不同的 APK。 組成部分 B…

26考研|數學分析:數項級數

數項級數這一章的開始&#xff0c;開啟了新的關于“級數”這一新的概念體系的學習進程&#xff0c;此部分共包含四章的內容&#xff0c;分別為數項級數、函數項級數、冪級數以及傅里葉級數。這一章中&#xff0c;首先要掌握級數的相關概念與定義&#xff0c;重難點在于掌握判斷…

擁抱健康生活,解鎖養生之道

在生活節奏日益加快的當下&#xff0c;健康養生已成為人們關注的焦點。科學的養生方法&#xff0c;能幫助我們增強體質、預防疾病&#xff0c;以更飽滿的精神狀態擁抱生活。 合理飲食是養生的基石。《黃帝內經》中提到 “五谷為養&#xff0c;五果為助&#xff0c;五畜為益&…

房地產安裝工程師簡歷模板

模板信息 簡歷范文名稱&#xff1a;房地產安裝工程師簡歷模板&#xff0c;所屬行業&#xff1a;其他 | 職位&#xff0c;模板編號&#xff1a;XUCP9X 專業的個人簡歷模板&#xff0c;邏輯清晰&#xff0c;排版簡潔美觀&#xff0c;讓你的個人簡歷顯得更專業&#xff0c;找到好…

HTML5 詳細學習筆記

1. HTML5 簡介 HTML5 是最新的 HTML 標準&#xff0c;于 2014 年 10 月由 W3C 完成標準制定。它增加了許多新特性&#xff0c;包括語義化標簽、多媒體支持、圖形效果、離線存儲等。 1.1 HTML5 文檔基本結構 <!DOCTYPE html> <html lang"zh-CN"> <h…

【網絡入侵檢測】基于Suricata源碼分析NFQ IPS模式實現

【作者主頁】只道當時是尋常 【專欄介紹】Suricata入侵檢測。專注網絡、主機安全,歡迎關注與評論。 1. 概要 ?? 本文聚焦于 Suricata 7.0.10 版本源碼,深入剖析其 NFQ(Netfilter Queue)模式的實現原理。通過系統性拆解初始化階段的配置流程、數據包監聽機制的構建邏輯,以…

C語言結構體和union內存對齊

在C語言的世界里&#xff0c;結構體&#xff08;struct&#xff09;和聯合體&#xff08;union&#xff09;的內存布局一直是困擾許多開發者的難題。當我們定義一個結構體時&#xff0c;編譯器會按照特定的規則為每個成員分配內存空間&#xff0c;這些規則被稱為內存對齊。看似…

本地部署DeepSeek-R1模型接入PyCharm

以下是DeepSeek-R1本地部署及接入PyCharm的詳細步驟指南,整合了視頻內容及官方文檔核心要點: 一、本地部署DeepSeek-R1模型 1. 安裝Ollama框架 ?下載安裝包 訪問Ollama官網(https://ollama.com/download)Windows用戶選擇.exe文件,macOS用戶選擇.dmg包。 ?安裝驗證 雙擊…

IEEE綜述 | 車道拓撲推理20年演進:從程序化建模到車載傳感器

導讀 車道拓撲推理對于高精建圖和自動駕駛應用至關重要&#xff0c;從早期的程序化建模方法發展到基于車載傳感器的方法&#xff0c;但是很少有工作對車道拓撲推理技術進行全面概述。為此&#xff0c;本文系統性地調研了車道拓撲推理技術&#xff0c;同時確定了未來研究的挑戰和…

開源模型應用落地-語音合成-MegaTTS3-零樣本克隆與多語言生成的突破

一、前言 在人工智能技術飛速發展的今天,文本轉語音(TTS)技術正以前所未有的速度改變著人機交互的方式。近日,字節跳動與浙江大學聯合推出了一款名為MegaTTS3 的開源TTS模型,再次刷新了行業對高質量語音合成的認知。作為一款輕量化設計的模型,MegaTTS3以僅0.45億參數 的規…

Python爬蟲實戰:移動端逆向工具Fiddler經典案例

一、引言 在移動互聯網迅猛發展的當下,移動端應用產生了海量的數據。對于開發者而言,獲取這些數據對于市場調研、競品分析、數據挖掘等工作具有重要意義。Fiddler 作為一款功能強大的 Web 調試代理工具,能夠有效捕獲、分析和修改移動端的網絡請求,為開發者深入了解移動端網…

AutoGPT超詳細教程

AutoGPT超詳細教程 AutoGPT 是一個強大的AI代理管理平臺&#xff0c;允許用戶通過直觀的界面構建、部署和自動化復雜工作流程。其核心是ForgeAgent&#xff0c;它管理代理邏輯、工具集成和任務執行&#xff0c;并通過文件存儲抽象層安全訪問文件。用戶可通過CLI創建代理、運行…

【Python網絡爬蟲實戰指南】從數據采集到反反爬策略

目錄 前言技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解核心作用講解關鍵技術模塊說明技術選型對比 二、實戰演示環境配置要求核心代碼實現案例1&#xff1a;靜態頁面抓取&#xff08;電商價格&#xff09;案例2&#xff1a;動態頁面抓取&…

矩陣運營的限流問題本質上是平臺與創作者之間的流量博弈

矩陣運營的限流問題本質上是平臺與創作者之間的流量博弈&#xff0c;要系統性解決這一問題&#xff0c;需從技術規避、內容優化、運營策略三個維度構建防御體系。以下結合平臺算法邏輯與實戰案例&#xff0c;深度解析限流成因及破解之道&#xff1a; 一、技術層&#xff1a;突…

【分布式理論17】分布式調度3:分布式架構-從中央式調度到共享狀態調度

文章目錄 一、中央式調度器1. 核心思想2. 工作流程3. 優缺點4. **典型案例&#xff1a;Google Borg** 二、兩級調度器1. **核心思想**2. **工作流程**3. 優缺點4. **典型案例&#xff1a;Hadoop YARN** 三、共享狀態調度器1. **核心思想**2. **工作流程**3. 優缺點4. **典型案例…

QSPI flash xip模式運行

背景&#xff1a; 在做一個項目&#xff0c;調研p-sram當ram用在cadence qspi接口下是否正常&#xff0c;首先用qspi-flash xip模式驗證控制器是否支持flash的xip模式。 一、更改步驟&#xff1a; 1.1首先配置鏈接腳本 默認鏈接腳本 OUTPUT_FORMAT("elf32-littlearm&q…

【C++】 —— 筆試刷題day_23

一、 打怪 題目解析 我們現在要去刷毛球怪&#xff0c;我的攻擊和血量是h和a、毛球怪的攻擊和血量是H和A&#xff1b; 我們和毛球怪的對決是輪流攻擊(我們先手)&#xff0c;當血量小于等于0時死亡&#xff1b; 現在我們要求在自己存活的條件下&#xff0c;最多能夠殺死幾只毛球…