從零到一:TCP 回聲服務器與客戶端的完整實現與原理詳解

目錄

一、TCP 通信的核心邏輯

二、TCP 服務器編程步驟

步驟 1:創建監聽 Socket

步驟 2:綁定地址與端口(bind)

步驟 3:設置監聽狀態(listen)

步驟 4:接收客戶端連接(accept)

步驟 5:與客戶端交互(read/write)

步驟 6:關閉連接(close)

步驟 7:并發處理(可選但重要)

三、TCP 客戶端編程步驟

步驟 1:創建客戶端 Socket

步驟 2:連接服務器(connect)

步驟 3:與服務器交互(read/write)

步驟 4:關閉連接

四、核心代碼解析

1. 輔助工具:InetAddr 類(網絡地址處理)

2. 服務器端實現:TcpServer 類

(1)初始化服務器:socket→bind→listen

(2)接收連接與處理請求:accept→read/write

3. 客戶端實現:TcpClient

4. 編譯腳本:Makefile

五、運行演示

步驟 1:編譯程序

步驟 2:啟動服務器

步驟 3:啟動客戶端(新終端)

步驟 4:交互測試

六、常見問題與解決方案

七、擴展與進階方向

八、總結


在網絡編程的學習旅程中,TCP 協議是繞不開的核心內容。它作為一種面向連接的可靠傳輸協議,支撐著互聯網中絕大多數的應用通信。本文將結合一套完整的 C++ 實現代碼,從基本原理到具體實踐,帶你掌握 TCP 編程的全流程 —— 從 socket 創建到多進程并發處理,最終實現一個可交互的 "回聲" 程序。

一、TCP 通信的核心邏輯

TCP(Transmission Control Protocol)的通信模型遵循固定的 "連接 - 傳輸 - 斷開" 流程,核心特點是面向連接可靠傳輸

  • 角色劃分:通信雙方分為服務器(被動等待連接)和客戶端(主動發起連接)
  • 連接建立:通過 "三次握手" 建立可靠連接,確保雙方都做好通信準備
  • 數據傳輸:基于字節流的方式傳輸數據,通過確認機制保證數據不丟失、不重復
  • 連接關閉:通過 "四次揮手" 優雅關閉連接,確保雙方數據都已傳輸完成

本次實現的 "回聲程序" 是 TCP 編程的經典入門案例:客戶端發送任意字符串,服務器接收后添加 "server echo#" 前綴返回,直觀展示完整通信流程。

二、TCP 服務器編程步驟

服務器的核心功能是 "監聽連接→接收請求→處理交互",完整步驟如下:

步驟 1:創建監聽 Socket

Socket(套接字)是網絡通信的 "門戶",本質是操作系統提供的網絡通信接口(文件描述符)。

// 創建TCP監聽Socket
int listensockfd = socket(AF_INET, SOCK_STREAM, 0);
if (listensockfd < 0) {// 錯誤處理:創建失敗(如協議不支持)perror("socket error");exit(1);
}

參數解析

  • AF_INET:使用 IPv4 地址族(互聯網最常用)
  • SOCK_STREAM:指定為流式套接字(TCP 協議的特征)
  • 0:自動選擇對應的數據傳輸協議(此處為 TCP)

步驟 2:綁定地址與端口(bind)

創建 Socket 后,需要將其與本機的具體 IP 和端口綁定,明確 "監聽哪個地址的請求"。

// 準備地址結構(網絡字節序)
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;                  // IPv4
local_addr.sin_port = htons(8081);                // 端口(主機字節序→網絡字節序)
local_addr.sin_addr.s_addr = INADDR_ANY;          // 綁定所有本地IP(多網卡場景適用)// 綁定操作
int ret = bind(listensockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));
if (ret < 0) {perror("bind error");exit(2);
}

關鍵細節

  • 網絡字節序:TCP 規定網絡中數據必須使用大端字節序,htons()(host to network short)用于端口轉換
  • INADDR_ANY:表示綁定本機所有可用 IP(無需手動指定具體 IP,靈活適配多網卡環境)

步驟 3:設置監聽狀態(listen)

綁定完成后,需將 Socket 轉為 "監聽狀態",準備接收客戶端的連接請求。

// 開始監聽(BACKLOG=8:未完成連接隊列的最大長度)
int ret = listen(listensockfd, 8);
if (ret < 0) {perror("listen error");exit(3);
}

BACKLOG 參數:限制正在進行三次握手(未完成連接)的最大數量,超過此值的新連接會被暫時拒絕。

步驟 4:接收客戶端連接(accept)

監聽狀態的 Socket 可以通過accept()函數阻塞等待并接收客戶端連接。

struct sockaddr_in client_addr;                  // 存儲客戶端地址
socklen_t client_addr_len = sizeof(client_addr);// 阻塞等待新連接,返回與該客戶端通信的Socket
int clientsockfd = accept(listensockfd, (struct sockaddr*)&client_addr, &client_addr_len);
if (clientsockfd < 0) {perror("accept error");continue;  // 忽略錯誤,繼續等待下一個連接
}

核心特性

  • accept()是阻塞函數,若無新連接則一直等待
  • 成功返回新的 Socket 描述符(專門用于與當前客戶端通信)
  • 原監聽 Socket(listensockfd)繼續用于接收其他連接

步驟 5:與客戶端交互(read/write)

連接建立后,通過read()write()實現數據收發。

char buffer[4096];
while (true) {// 讀取客戶端數據ssize_t n = read(clientsockfd, buffer, sizeof(buffer) - 1);if (n > 0) {  // 讀取成功buffer[n] = '\0';  // 手動添加字符串結束符// 處理數據(示例:添加前綴后回送)std::string response = "server: " + std::string(buffer);write(clientsockfd, response.c_str(), response.size());}else if (n == 0) {  // 客戶端主動關閉連接std::cout << "client closed" << std::endl;break;}else {  // 讀取錯誤(如網絡異常)perror("read error");break;}
}

注意事項

  • read()返回值需嚴格判斷:正數為實際讀取字節數,0 表示對方關閉,負數表示錯誤
  • 避免假設 " 一次read()能獲取完整數據 "(TCP 是流式協議,數據可能分多次到達)

步驟 6:關閉連接(close)

交互結束后,關閉 Socket 釋放資源:

close(clientsockfd);  // 關閉與客戶端的連接Socket
// 服務器退出時關閉監聽Socket
// close(listensockfd);

步驟 7:并發處理(可選但重要)

單進程服務器一次只能處理一個客戶端,實際應用中需支持并發,常用方案:

  • 多進程:通過fork()創建子進程處理每個連接(隔離性好,資源消耗高)
  • 多線程:通過pthread_create()創建線程(資源消耗低,需處理同步)
  • IO 多路復用:用select/epoll(Linux)實現單進程處理多連接(高性能)

三、TCP 客戶端編程步驟

客戶端流程相對簡單,核心是 "連接服務器→交互數據":

步驟 1:創建客戶端 Socket

與服務器類似,客戶端也需要創建 Socket:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {perror("socket error");exit(1);
}

步驟 2:連接服務器(connect)

客戶端通過connect()向服務器發起連接請求(觸發三次握手)。

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8081);                  // 服務器端口
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服務器IPint ret = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (ret < 0) {perror("connect error");exit(1);
}

特點connect()是阻塞函數,直到連接建立或失敗才返回。

步驟 3:與服務器交互(read/write)

連接成功后,通過read()/write()與服務器通信:

std::string message;
char buffer[1024];
while (true) {// 輸入要發送的數據std::cout << "input message: ";getline(std::cin, message);// 發送數據ssize_t n = write(sockfd, message.c_str(), message.size());if (n <= 0) break;// 接收服務器響應int m = read(sockfd, buffer, sizeof(buffer));if (m > 0) {buffer[m] = '\0';std::cout << "server response: " << buffer << std::endl;} else break;
}

步驟 4:關閉連接

close(sockfd);

四、核心代碼解析

1. 輔助工具:InetAddr 類(網絡地址處理)

網絡編程中,IP 地址和端口需要在 "網絡字節序"(大端)和 "主機字節序"(可能為小端)之間轉換,InetAddr類封裝了這一高頻操作:

#pragma once
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define CONV(v) (struct sockaddr *)(v)class InetAddr {
private:struct sockaddr_in _net_addr;  // 網絡字節序的地址結構std::string _ip;               // 主機字節序的IP字符串uint16_t _port;                // 主機字節序的端口號// 端口從網絡字節序轉主機字節序void PortNet2Host() { _port = ::ntohs(_net_addr.sin_port); }// IP從網絡字節序轉主機字節序(點分十進制字符串)void IpNet2Host() {char ipbuffer[64];::inet_ntop(AF_INET, &_net_addr.sin_addr, ipbuffer, sizeof(ipbuffer));_ip = ipbuffer;}public:// 從sockaddr_in初始化(接收客戶端連接時使用)InetAddr(const struct sockaddr_in &addr) : _net_addr(addr) {PortNet2Host();IpNet2Host();}// 獲取IP:Port格式字符串(如127.0.0.1:8081)std::string Addr() { return Ip() + ":" + std::to_string(Port()); }// 其他實用接口std::string Ip() { return _ip; }uint16_t Port() { return _port; }struct sockaddr *NetAddr() { return CONV(&_net_addr); }
};

核心作用:自動完成地址轉換,讓業務代碼更簡潔,避免重復處理字節序問題。

2. 服務器端實現:TcpServer 類

服務器的核心工作是 "監聽連接→接收請求→處理請求",TcpServer類封裝了完整流程:

(1)初始化服務器:socket→bind→listen
class TcpServer {
private:uint16_t _port;         // 端口號bool _running;          // 運行狀態標識int _listensockfd;      // 監聽socket描述符public:TcpServer(int port = 8081) : _port(port), _running(false) {}void InitServer() {// 1. 創建監聽socket(AF_INET:IPv4,SOCK_STREAM:TCP)_listensockfd = socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0) {std::cout << "socket error" << std::endl;exit(1);}// 2. 綁定地址(IP+端口)struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;         // IPv4協議local.sin_port = htons(_port);      // 端口轉網絡字節序local.sin_addr.s_addr = INADDR_ANY; // 綁定所有本地IP(多網卡場景適用)int n = bind(_listensockfd, CONV(&local), sizeof(local));if (n < 0) {std::cout << "bind error" << std::endl;exit(2);}// 3. 開始監聽(BACKLOG=8:未完成連接隊列最大長度)n = listen(_listensockfd, 8);if (n < 0) {std::cout << "listen error" << std::endl;exit(3);}}
};

關鍵函數解析

  • socket():創建用于網絡通信的文件描述符(類似文件句柄),參數指定協議族(IPv4)和協議類型(TCP)
  • bind():將 socket 與特定地址綁定,INADDR_ANY表示監聽本機所有 IP
  • listen():將 socket 轉為監聽狀態,BACKLOG限制同時建立連接的最大數量
(2)接收連接與處理請求:accept→read/write
class TcpServer {// ... 省略前面代碼 ...public:void Start() {_running = true;while (_running) {// 接收客戶端連接(阻塞等待新連接)struct sockaddr_in peer;socklen_t peerlen = sizeof(peer);int sockfd = accept(_listensockfd, CONV(&peer), &peerlen);if (sockfd < 0) {std::cout << "accept error" << std::endl;continue;}// 打印客戶端地址InetAddr addr(peer);std::cout << "client into: " << addr.Addr() << std::endl;// 多進程處理并發(核心)pid_t id = fork();if (id == 0) {  // 子進程close(_listensockfd);  // 子進程不需要監聽socket// 二次fork:避免子進程成為僵尸進程(讓孫子進程被系統收養)if (fork() > 0) exit(0);HandlerRequest(sockfd);  // 處理當前客戶端請求exit(0);}close(sockfd);  // 父進程關閉連接socketwaitpid(id, NULL, 0);  // 回收子進程資源}}// 處理客戶端請求(回聲邏輯)void HandlerRequest(int sockfd) {char inbuffer[4096];while (true) {// 讀取客戶端數據ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);if (n > 0) {  // 讀取成功inbuffer[n] = 0;  // 手動添加字符串結束符std::string echo_str = "server echo#" + std::string(inbuffer);write(sockfd, echo_str.c_str(), echo_str.size());  // 回送數據std::cout << "server echo: " << inbuffer << std::endl;}else if (n == 0) {  // 客戶端關閉連接std::cout << "client closed: " << sockfd << std::endl;break;}else {  // 讀取錯誤break;}}close(sockfd);  // 關閉連接}
};

核心邏輯說明

  • accept():阻塞等待客戶端連接,返回新的 socket 描述符(專門用于與該客戶端通信)
  • 多進程并發:通過fork()創建子進程處理每個客戶端,父進程繼續接收新連接;二次fork()避免僵尸進程(子進程退出后由系統回收)
  • 數據處理:read()讀取客戶端數據,write()回送帶前綴的回聲,通過返回值判斷通信狀態(成功 / 關閉 / 錯誤)

3. 客戶端實現:TcpClient

客戶端流程相對簡單,核心是 "創建 socket→連接服務器→收發數據":

#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>int main(int argc, char *argv[]) {// 解析命令行參數(服務器IP和端口)if (argc != 3) {std::cout << "Usage:./TcpClient <server_ip> <port>" << std::endl;return 1;}std::string server_ip = argv[1];int server_port = std::stoi(argv[2]);// 1. 創建客戶端socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {std::cout << "Error in creating socket" << std::endl;return 1;}// 2. 連接服務器(觸發三次握手)struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(server_port);  // 端口轉網絡字節序server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());  // IP轉網絡字節序int n = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (n < 0) {std::cout << "Error in connecting to server" << std::endl;return 1;}// 3. 循環發送數據并接收回聲std::string message;while (true) {char inbuffer[1024];std::cout << "input message to send to server: ";getline(std::cin, message);// 發送數據到服務器n = write(sockfd, message.c_str(), message.size());if (n <= 0) break;// 接收服務器回聲int m = read(sockfd, inbuffer, 1024);if (m > 0) {inbuffer[m] = '\0';std::cout << "Server response: " << inbuffer << std::endl;} else break;}close(sockfd);  // 關閉連接return 0;
}

關鍵函數connect()會觸發 TCP 三次握手,阻塞直到連接建立或失敗;成功后通過read()/write()與服務器交互。

4. 編譯腳本:Makefile

為簡化編譯流程,使用 Makefile 一鍵生成服務器和客戶端可執行文件:

.PHONY:all
all:server_tcp client_tcp  # 目標:服務器和客戶端# 編譯服務器(依賴TcpServer.cc,鏈接pthread庫)
server_tcp:TcpServer.ccg++ -o $@ $^ -std=c++17 -lpthread# 編譯客戶端(依賴TcpClient.cc)
client_tcp:TcpClient.ccg++ -o $@ $^ -std=c++17 -lpthread.PHONY:clean
clean:  # 清理生成的文件rm -f client_tcp server_tcp

五、運行演示

步驟 1:編譯程序

make  # 生成server_tcp(服務器)和client_tcp(客戶端)

步驟 2:啟動服務器

./server_tcp  # 默認監聽8081端口

步驟 3:啟動客戶端(新終端)

./client_tcp 127.0.0.1 8081  # 連接本地服務器(127.0.0.1為本地回環地址)

步驟 4:交互測試

在客戶端輸入任意內容(如 "hello tcp"),會收到服務器返回的 "server echo#hello tcp";服務器終端會同步打印接收的消息,效果如下:

# 客戶端終端
input message to send to server: hello tcp
Server response: server echo#hello tcp# 服務器終端
client into: 127.0.0.1:54321  # 客戶端端口為隨機分配
server echo: hello tcp

六、常見問題與解決方案

  1. 地址已在使用(Address already in use)

    • 原因:服務器關閉后,端口會進入 TIME_WAIT 狀態(默認保留 2MSL 時間),短期內無法重用
    • 解決:創建 socket 后設置 SO_REUSEADDR 選項,允許端口重用:
    int opt = 1;
    setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    
  2. 僵尸進程問題

    • 原因:子進程退出后,父進程未及時回收其資源,導致進程殘留
    • 解決:除了代碼中的二次fork(),還可注冊 SIGCHLD 信號處理函數,自動回收子進程:
    signal(SIGCHLD, SIG_IGN);  // 忽略SIGCHLD信號,系統自動回收子進程
    
  3. 粘包問題

    • 原因:TCP 是流式協議,數據無邊界,多次發送的小數據可能被合并傳輸
    • 解決:定義應用層協議(如 "數據長度 + 實際數據" 格式),確保接收方正確拆分數據。

七、擴展與進階方向

  1. 錯誤處理增強:當前用cout輸出錯誤,可改用perror()結合errno打印更詳細的錯誤原因(如 "bind error: Address already in use")。

  2. 線程池替代多進程:多進程資源消耗高,可改用線程池(提前創建固定數量的線程),減少動態創建銷毀的開銷。

  3. 配置化參數:將端口、BACKLOG 等參數通過命令行或配置文件傳入,避免硬編碼(如./server_tcp -p 8080 -b 16)。

  4. 功能擴展:基于現有框架實現文件傳輸(分塊發送文件內容)、多客戶端群聊(服務器轉發消息)等功能。

  5. IO 多路復用:使用select/poll/epoll(Linux)實現單進程處理多連接,大幅提升并發性能(適用于高并發場景)。

八、總結

本文通過一個完整的 TCP 回聲程序,展示了網絡編程的核心流程:從socket創建、bind綁定、listen監聽,到accept接收連接、read/write收發數據,再到多進程并發處理。這些基礎操作是理解 HTTP 服務器、RPC 框架等復雜網絡應用的基石。

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

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

相關文章

MyBatis-Plus核心內容

MyBatis-Plus MyBatis-Plus 是一個基于 MyBatis的增強工具&#xff0c;旨在簡化開發過程&#xff0c;減少重復代碼。它在MyBatis的基礎上增加了CRUD操作封裝&#xff0c;條件構造器、代碼生成器等功能。 一、核心特性與優勢 1. 核心特性 無侵入&#xff1a;只做增強不做改變&am…

計算機網絡摘星題庫800題筆記 第4章 網絡層

第4章 網絡層4.1 網絡層概述題組闖關1.在 Windows 的網絡配置中&#xff0c;“默認網關” 一般被設置為 ( ) 的地址。 A. DNS 服務器 B. Web 服務器 C. 路由器 D. 交換機1.【參考答案】C 【解析】只有在計算機上正確安裝網卡驅動程序和網絡協議&#xff0c;并正確設置 IP 地址信…

非root用戶在linux中配置zsh(已解決ncurses-devel報錯)

Zsh&#xff08;Z Shell&#xff09;是一款功能強大的交互式 Unix shell&#xff0c;以其高度可定制性和豐富的功能著稱&#xff0c;被視為 Bash 的增強替代品。它支持智能補全、主題美化、插件擴展&#xff08;如 Oh My Zsh 框架&#xff09;、自動糾錯、全局別名等特性&#…

《Foundations and Recent Trends in Multimodal Mobile Agents: A Survey》論文精讀筆記

論文鏈接&#xff1a;https://arxiv.org/pdf/2411.02006 摘要 文章首先介紹了核心組件&#xff0c;并探討了移動基準和交互環境中的關鍵代表性作品&#xff0c;旨在全面理解研究重點及其局限性。 接著&#xff0c;將這些進展分為兩種主要方法&#xff1a; 基于提示的方法&a…

npm安裝時一直卡住的解決方法

npm install 卡住通常是由于網絡問題或緩存問題導致的。以下是幾種解決方法&#xff1a; 方法1&#xff1a;清理npm緩存 npm cache clean --force npm install方法2&#xff1a;刪除node_modules和package-lock.json重新安裝 rm -rf node_modules package-lock.json npm instal…

[密碼學實戰]使用Java生成國密SM2加密證書等(四十三)

[密碼學實戰]使用Java生成國密SM2加密證書等(四十三) 本文將詳細介紹如何通過Java代碼生成符合國密標準的SM2加密證書,包括密鑰對生成、證書擴展屬性配置、PEM格式保存等關鍵步驟。 一. 運行結果示例 二. 國密算法與加密證書 國密算法(SM系列)是中國自主研發的密碼算法體…

從零開始之stm32之CAN通信

從小白的視角了解并實現簡單的STM32F103的CAN通信&#xff0c;直接上手。一、CAN協議簡介CAN總線上傳輸的信息稱為報文&#xff0c;當總線空閑時任何連接的單元都可以開始發送新的報文&#xff0c;有5種類型的幀&#xff1a;數據幀、遙控幀、錯誤幀、過載幀、幀間隔。數據幀有兩…

Java 課程,每天解讀一個簡單Java之利用條件運算符的嵌套來完成此題:學習成績>=90分的同學用A表示,60-89分之間的用B表示, * 60分以下

package ytr250812;/*題目&#xff1a;利用條件運算符的嵌套來完成此題&#xff1a;學習成績>90分的同學用A表示&#xff0c;60-89分之間的用B表示&#xff0c;* 60分以下*/import java.util.Scanner;public class GradeEvaluator {public static void main(String[] args) …

Word XML 批注范圍克隆處理器

該類用于處理 Word 文檔&#xff08;XML 結構&#xff09;中被批注標記的文本范圍&#xff0c; 實現指定內容的深度克隆&#xff0c;并將其插入到目標節點之后。 適用于在生成或修改 .docx 文件時復制批注內容塊。/*** Word XML 批注范圍克隆處理器* * 該類用于處理 Word 文檔&…

MQTT:Java集成MQTT

目錄Git項目路徑一、原生java架構1.1 導入POM文件1.2 編寫測試用例二、SpringBoot集成MQTT2.1 導入POM文件2.2 在YML文件中增加配置2.3 新建Properties配置文件映射配置2.4 創建連接工廠2.5 增加入站規則配置2.6 增加出站規則配置2.7 創建消息發送網關2.8 測試消息發送2.9 項目…

day 16 stm32 IIC

1.IIC概述1基于對話的形式完成&#xff0c;不需要同時進行發送和接收所以刪掉了一根數據線&#xff0c;變成半雙工2為了安全起見添加了應答機制3可以接多個模塊&#xff0c;且互不干擾4異步時序&#xff0c;要求嚴格&#xff0c;發送過程中不能暫停&#xff0c;所以需要同步時序…

AMD KFD的BO設計分析系列 0:開篇

開啟我始終不敢碰的GPU存儲系列&#xff0c;先上個圖把核心關系表達下&#xff0c;以此紀念。注&#xff1a;圖中kfdm_mm誤寫&#xff0c;應該為kfd_mm&#xff0c;不修改了&#xff0c;請大家不要介意。

EUDR的核心內容,EUDR認證的好處,EUDR意義

近年來&#xff0c;全球森林退化問題日益嚴峻&#xff0c;毀林行為不僅加劇氣候變化&#xff0c;還威脅生物多樣性和原住民權益。為應對這一挑戰&#xff0c;歐盟于2023年6月正式實施《歐盟零毀林法案》&#xff08;EU Deforestation-free Regulation, EUDR&#xff09;&#x…

數據分析專欄記錄之 -基礎數學與統計知識

數據分析專欄記錄之 -基礎數學與統計知識&#xff1a; 1、描述性統計 均值 data_set [10, 20, 30, 40, 50] mean sum(data_set)/len(data_set)np 里面的函數&#xff0c;對二維進行操作時&#xff0c; 默認每一列 mean1 np.mean(data_set) print(mean, mean1)s 0 for i…

《星辰建造師:C++多重繼承的奇幻史詩》

&#x1f30c;&#x1f525; 《星辰建造師&#xff1a;多重繼承與this指針的終極史詩》 &#x1f525;&#x1f30c;—— 一場融合魔法、科技與哲學的C奇幻冒險&#x1f320;&#x1f30c; 序章&#xff1a;代碼宇宙的誕生 &#x1f30c;&#x1f320;在無盡的代碼維度中&#…

云計算-OpenStack 運維開發實戰:從 Restful API 到 Python SDK 全場景實現鏡像上傳、用戶創建、云主機部署全流程

一、python-Restful Api 簡介 Restful API 是一種軟件架構風格,基于 HTTP 協議設計,通過統一的接口(如 URL 路徑)和標準的 HTTP 方法(GET/POST/PUT/DELETE 等)實現資源(如數據、文件等)的操作,具有無狀態、可緩存、客戶端 - 服務器分離等特點。方法如下 用 GET 請求獲…

RxJava 在 Android 中的深入解析:使用、原理與最佳實踐

前言RxJava 是一個基于觀察者模式的響應式編程庫&#xff0c;它通過可觀察序列和函數式操作符的組合&#xff0c;簡化了異步和事件驅動程序的開發。在 Android 開發中&#xff0c;RxJava 因其強大的異步處理能力和簡潔的代碼風格而廣受歡迎。本文將深入探討 RxJava 的使用、核心…

面試實戰 問題三十 HTTP協議中TCP三次握手與四次揮手詳解

HTTP協議中TCP三次握手與四次揮手詳解 在HTTP協議中&#xff0c;連接建立和斷開依賴于底層的TCP協議。雖然HTTP本身不定義握手過程&#xff0c;但所有HTTP通信都通過TCP三次握手建立連接&#xff0c;通過四次揮手斷開連接。以下是詳細解析&#xff1a;一、TCP三次握手&#xff…

讀《精益數據分析》:雙邊市場的核心指標分析

雙邊市場數據分析指南&#xff1a;從指標體系到實戰落地&#xff08;基于《精益數據分析》框架&#xff09;在互聯網平臺經濟中&#xff0c;雙邊市場&#xff08;如電商、出行、外賣、自由職業平臺等&#xff09;的核心矛盾始終是"供需平衡與效率優化"。這類平臺連接…

Queue參考代碼

queue.c #include "queue.h" #include "stdlib.h" // 初始化循環隊列 void initializeCircularQueue(CircularQueue *cq, uint8_t *buffer, uint32_t size) {cq->front 0;cq->rear 0;cq->count 0;cq->size size;cq->data buffer; }…