快速入門Socket編程——封裝一套便捷的Socket編程——導論

快速入門Socket編程——封裝一套便捷的Socket編程——導論

前言

? 這里是筆者打算做的Socket編程的第二部分,也就是核心的討論我們Socket編程本身。

導論

? 我們知道,一個經典的服務器套接字的處理流程是如下的:

  1. 創建一個指定傳輸層和網絡層協議的套接字(socket)
  2. 申請和綁定操作系統到指定的端口上(bind)
  3. 運行使能申請到的資源,也就是監聽(listen)
  4. 接受和開啟對客戶端之間的通信(accept后做read && write。完成業務后如果需要關閉則關閉之)

? 對于客戶端,事情就會簡單一些,我們只需要創建套接字(socket)后指定要連接的遠程對象就好(connect),之后就可以跟服務器之間做IO通信了。

? 那么,我們就是準備好做封裝。封裝之前就需要了解一下基本的系統API,這里需要注意的是,我們只討論Linux系列的Socket API,盡管Windows在一定層次上對我們的Socket編程接口存在兼容,但是仍有不少的差距(比如說必須加載Socket編程庫,完事后還有Cleanup,關閉套接字的API也跟Linux的存在差距)

socket創建一個套接字

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

? 這個API實際上就是創建了一個指定了IP版本協議,傳輸層協議簇和傳輸層協議的API,

? domain參數描述的是我們的IP網絡層協議采用的決定,這個參數指定了套接字使用的地址族(Address Family),它決定了套接字可以與哪種類型的網絡進行通信。最常見的選項有:

  • AF_INET:用于 IPv4 協議。這是目前最常用的選項,它允許您使用 32 位的 IP 地址進行通信。
  • AF_INET6:用于 IPv6 協議。如果您需要使用 128 位的 IP 地址,則應選擇此選項。
  • AF_UNIX:用于 本地進程間通信(IPC)。它不涉及網絡,而是在同一臺機器的不同進程間進行通信,效率更高。

? 一般而言,我們會采用的是AF_INET,這個我想大家最熟悉。

? type定義了套接字的服務類型,也就是數據傳輸的方式。這里說的是傳輸層我們采納的協議控制。

  • SOCK_STREAM流式套接字。它提供可靠的、面向連接的通信服務,使用 TCP (Transmission Control Protocol) 協議。數據會按順序、無差錯地傳輸,適用于網頁瀏覽、文件傳輸等需要高可靠性的場景。(面向連接的)
  • SOCK_DGRAM數據報套接字。它提供不可靠的、無連接的通信服務,使用 UDP (User Datagram Protocol) 協議。數據報可能會丟失、重復或亂序到達,但它具有低延遲的特點,適用于實時音視頻、在線游戲等對實時性要求高但允許少量數據丟失的場景。(面向數據的)
  • SOCK_RAW原始套接字。這種類型的套接字允許您直接訪問 IP 層,可以自己構造 IP 數據包,常用于網絡協議分析工具(如 ping)或一些特殊的網絡應用。

? 對于protocol這個參數用于指定在特定的協議族和套接字類型下的具體協議。通常情況下,咱都是將其設置為 0

protocol0 時,系統會根據 domaintype 的組合自動選擇最合適的默認協議。例如:

  • socket(AF_INET, SOCK_STREAM, 0):系統會自動選擇 TCP 協議。
  • socket(AF_INET, SOCK_DGRAM, 0):系統會自動選擇 UDP 協議。

只有在某些特殊情況下(例如使用 SOCK_RAW),您才需要顯式指定協議編號。

bind監聽一個端口

? bind在我導論的時候就說過,實際上就是申請且綁定應用程序需要的端口。它負責將一個創建好的套接字(Socket)與一個特定的本地 IP 地址和端口號關聯起來。簡單來說,就是給你的套接字在網絡世界中“分配一個門牌號”。

?

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

? bind函數的接口比較復雜,第一個填入的就是套接字的文件描述符,這個沒啥可說的,因為我們就是需要我們的套接字具備可用的端口。

? 需要知道的是這類API都會返回負數表示失敗,所以可以檢查一下,這個函數很容易失敗,因為我們要是退出服務的時候沒有正確的釋放端口資源(或者說,存在比較長的TIME_WAIT的端口),就會沒辦法進行再次綁定。

? struct sockaddr *addr是一個指向 struct sockaddr 類型常量的指針。struct sockaddr 是一個通用的套接字地址結構體,但它是一個泛型結構,實際使用中,我們通常會將其轉換為更具體的地址結構體,例如:

  • struct sockaddr_in:用于 IPv4 地址。
  • struct sockaddr_in6:用于 IPv6 地址。
  • struct sockaddr_un:用于 UNIX 域套接字(本地進程間通信)。

這個結構體中包含了我們希望綁定的 IP 地址端口號

#include <netinet/in.h> // 包含 sockaddr_in 的定義
#include <arpa/inet.h>  // 包含 inet_addr() 和 htons()struct sockaddr_in {sa_family_t    sin_family; // 地址族,通常設置為 AF_INETin_port_t      sin_port;   // 端口號,必須是網絡字節序struct in_addr sin_addr;   // IP 地址,必須是網絡字節序// char           sin_zero[8]; // 填充字節,通常不需要顯式設置
};
struct in_addr {in_addr_t s_addr; // 32位IPv4地址
};

在設置 sin_portsin_addr 時,請務必注意字節序轉換。網絡上的數據傳輸通常使用網絡字節序(大端字節序),而我們的主機可能使用主機字節序(大端或小端)。為了確保不同系統間的兼容性,必須進行轉換:

  • htons() (host to network short): 將主機字節序的短整型(通常是端口號)轉換為網絡字節序。
  • htonl() (host to network long): 將主機字節序的長整型(通常是 IP 地址)轉換為網絡字節序。
  • inet_addr()inet_pton(): 將點分十進制的 IP 地址字符串轉換為網絡字節序的二進制形式,并存儲到 in_addr.s_addr 中。

? 所以,我們可能需要正確的轉換排序的大小端格式,這個時候,咱們的確需要做的就是htons或者是htonl。

? 剩下的參數填寫的是采用的結構體的大小,這個是純粹方便正確的轉換的,填寫sizeof的結果就完事了

題外話:避免“地址已使用”錯誤 (EADDRINUSE):

在服務器程序中,當程序崩潰或異常退出時,操作系統可能不會立即釋放綁定的端口,導致在短時間內重新啟動程序時報告“地址已使用”錯誤。為了避免這個問題,通常會在 bind() 之前設置套接字的SO_REUSEADDR 選項,允許重新使用處于 TIME_WAIT 狀態的本地地址。這通過 setsockopt() 函數實現。

int optval = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {perror("setsockopt SO_REUSEADDR failed");// 處理錯誤
}

這樣的話,我們就不會再次觸發端口已經綁定的問題。

bind的本質是為服務器指定監聽地址和端口:對于服務器程序而言,bind() 是必不可少的一步。它告訴操作系統,這個套接字將監聽哪個 IP 地址上的哪個端口號。只有綁定了地址和端口,客戶端才能找到并連接到服務器。

  • IP 地址
    • 如果您想讓服務器監聽所有可用的網絡接口(即所有本機 IP 地址),可以將 sin_addr.s_addr 設置為 INADDR_ANY(通常是 0.0.0.0,經過 htonl() 轉換后)。
    • 如果您想讓服務器只監聽特定的一個 IP 地址,可以將其設置為該 IP 地址的 inet_addr() 轉換結果。
  • 端口號
    • 端口號范圍是 065535
    • 0-1023知名端口,通常由系統服務占用,需要 root 權限才能綁定。
    • 1024-49151注冊端口
    • 49152-65535動態/私有端口,通常用于客戶端的臨時端口。
    • 作為服務器,您通常會選擇一個大于 1023 的固定端口號。

listen:將通信激活到監聽狀態

? 我們知道,下一步就是驅動我們的網卡監聽外部信息,去嘗試捕捉潛在的客戶端的連接。

#include <sys/socket.h>
int listen(int sockfd, int backlog);

? 這是我們的API接口,sockfd是已經通過 socket() 函數創建并通過 bind() 函數綁定了本地地址和端口號的套接字文件描述符。listen() 函數將使這個特定的套接字開始監聽網絡連接。

注意: 只有流式套接字(SOCK_STREAM)才需要調用 listen()。數據報套接字(SOCK_DGRAM)是無連接的,因此不需要監聽。這個算是一個需要注意的點!

backlog(待處理連接隊列的最大長度)

? 這個參數是 listen() 函數的核心,它指定了系統可以為這個套接字排隊等待接受的連接請求數量。為了理解 backlog,我們需要知道 TCP 連接建立的三次握手過程。當一個客戶端發起連接請求時,它會向服務器發送一個 SYN 報文。此時,操作系統會創建一個連接,并將其放在一個半連接隊列SYN queue)中。當服務器收到客戶端的 ACK 報文,完成三次握手后,這個連接會被從半連接隊列移動到全連接隊列accept queue)。

backlog 參數的真正作用就是限制這個“全連接隊列”的最大長度。

  • 如果全連接隊列已滿:當有新的客戶端連接完成三次握手時,操作系統會忽略其 ACK 報文,導致客戶端最終超時,認為連接失敗。
  • backlog 的值
    • 歷史上,不同的操作系統對 backlog 的解釋和實現有所不同。在現代 Linux 系統中,backlog 參數主要控制的就是全連接隊列的最大長度。
    • 如何選擇 backlog 的值? 應該根據你的服務器性能和預期的并發連接數來決定。如果你的服務器可能在短時間內收到大量的連接請求,一個較大的 backlog 值可以防止新連接被拒絕,直到你的程序有時間調用 accept() 來處理它們。
    • 如果 backlog 值設置為 0,某些系統可能將其視為默認值,而另一些系統則可能導致無法接受任何連接。因此,最好設置一個合理的值,例如 10128SOMAXCONN(通常是系統定義的最大值)。

accept:接受一個客戶端的連接

在 Linux 網絡編程中,accept() 函數是服務器程序接收客戶端連接的“握手”操作。在 listen() 函數使套接字進入監聽狀態并準備好連接隊列后,accept() 的作用就是從這個隊列中取出最靠前的一個連接請求,并創建一個新的套接字專門用于與該客戶端進行通信。

accept() 函數的原型定義在 <sys/socket.h> 頭文件中:

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept() 是一個阻塞函數。這意味著如果當前全連接隊列中沒有等待的連接,它會一直阻塞在那里,直到有新的客戶端連接完成三次握手。如果 sockfd 被設置為非阻塞模式,accept() 會立即返回 -1,并設置 errnoEAGAINEWOULDBLOCK。成功時,它返回一個新的套接字文件描述符,這個描述符專門用來與發起連接的客戶端進行數據傳輸。失敗時,它返回 -1

1. sockfd(監聽套接字文件描述符)

這是通過 socket()bind() 創建,并用 listen() 進入監聽狀態的服務器監聽套接字accept() 不會在這個套接字上進行數據收發,它只是用它來接收連接請求。

2. addr(客戶端地址結構體)

這是一個指向 struct sockaddr 類型結構體的指針。accept() 函數會填充這個結構體,存儲發起連接的客戶端的地址信息,包括其 IP 地址和端口號。

通常,您會聲明一個 struct sockaddr_in(用于 IPv4)類型的變量,然后將其地址強制轉換為 struct sockaddr * 傳遞給 accept()

3. addrlen(地址結構體長度)

這是一個指向 socklen_t 類型的指針。在調用 accept() 之前,您需要將 addrlen 指向的變量設置為 addr 結構體的初始大小accept() 函數執行完畢后,它會更新 addrlen 指向的值,使其反映出 addr 結構體中實際存儲的有效字節數。

? 下面的代碼就是一個最簡單的服務器端的通信代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define PORT 8080int main() {int server_fd, new_socket;struct sockaddr_in address;socklen_t addrlen = sizeof(address);char buffer[1024] = {0};// ... socket() 和 bind() 和 listen() ...if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// ... bind and listen ...if (listen(server_fd, 10) < 0) {perror("listen failed");exit(EXIT_FAILURE);}printf("Server listening on port %d\n", PORT);// 4. 持續接受連接while (1) {printf("Waiting for a connection...\n");// 接受一個連接,如果隊列為空則阻塞if ((new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) {perror("accept failed");// 可以選擇繼續循環或者退出continue;}// 打印客戶端信息printf("Connection accepted from %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));// 在這里,服務器可以使用 new_socket 與客戶端進行通信read(new_socket, buffer, 1024);printf("Client sent: %s\n", buffer);const char *hello = "Hello from server";send(new_socket, hello, strlen(hello), 0);printf("Hello message sent\n");// 通信結束后,關閉新套接字close(new_socket);printf("Connection closed.\n");}close(server_fd);return 0;
}

題外話:TCP 三次握手與 Linux Socket API

客戶端 (Client)

  1. connect() 函數調用:當客戶端調用 connect() 函數時,操作系統會發送第一個 SYN 包給服務器,請求建立連接。此時,客戶端進入 SYN_SENT 狀態,等待服務器的回應。
  2. connect() 函數返回:客戶端的 connect() 函數會阻塞(在默認情況下),直到收到服務器的 SYN-ACK 包和客戶端自己的 ACK 包成功發送后,也就是三次握手完成,connect() 函數才會返回。此時,連接已經建立,客戶端進入 ESTABLISHED 狀態。

服務器端 (Server)

  1. listen() 函數調用:listen() 函數本身只是告訴操作系統,這個套接字已準備好接受連接。它會設置一個待處理連接隊列,但并不會立即開始三次握手。
  2. 接收 SYN 包:當客戶端調用 connect() 發送第一個 SYN 包后,服務器端的操作系統內核會被動地接收這個 SYN 包。內核會回應一個 SYN-ACK 包,并創建連接的半連接狀態,將其放入半連接隊列中。這個過程是由內核自動完成的,不涉及任何應用程序級別的 API 調用。
  3. 接收 ACK 包并移動到全連接隊列:當服務器收到客戶端發來的最后一個 ACK 包后,三次握手完成。操作系統會將該連接從半連接隊列中移動到全連接隊列backlog)。這個過程也是由內核自動完成的。
  4. accept() 函數調用:服務器調用 accept() 函數時,它會從全連接隊列中取出一個已經完成三次握手的連接。如果隊列為空,accept() 會阻塞等待。當 accept() 成功返回時,就意味著它已經拿到一個建立好的連接,可以開始進行數據通信了。

? 換而言之,所有的三次握手不發生在accept中,對于服務器端,他早在accept返回之前就完成了連接(因為如果沒有完成,他會等待直到連接完成后,才會取出來這個套接字返回進行IO操作,三次握手的主動方在客戶端的connect上,connect調用發起的時候進行第一次SYN握手,等待服務器完成接受SYN報文且回復ACK-SYN報文,connect接受到這個報文后再次回復好ACK才會跳出connect函數準備進行通信

總結

三次握手步驟客戶端 API服務器端 API備注
第一次握手connect() 內部發送 SYN內核被動接收客戶端發起連接請求
第二次握手內核被動接收 SYN-ACK內核被動發送 SYN-ACK服務器確認收到,并回應確認和自己的連接請求
第三次握手內核被動發送 ACKconnect() 返回內核被動接收 ACK 包 連接進入全連接隊列客戶端確認,連接建立,服務器端可被 accept() 接收

因此,三次握手這個復雜的協議過程,在 Linux C Socket 編程中,被巧妙地封裝在了 connect()accept() 這兩個 API 的阻塞行為中。connect() 阻塞直到連接建立,而 accept() 阻塞直到有完成握手的連接可以被接受。

客戶端的connect函數

? 我們這里再把最后的connect函數說一下:

connect() 函數的原型定義在 <sys/socket.h> 頭文件中:

#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect() 是一個阻塞函數(在默認情況下)。它會一直等待,直到連接建立成功或發生錯誤。成功時,它返回 0;失敗時,返回 -1,并設置全局變量 errno

1. sockfd(套接字文件描述符)

這是通過 socket() 函數創建的套接字文件描述符。connect() 函數將使用這個套接字來發起連接請求。

2. addr(服務器地址結構體)

這是一個指向 struct sockaddr 類型常量的指針。它包含了你想要連接的服務器的地址信息,包括其 IP 地址和端口號。

bind() 類似,實際使用中我們會用更具體的地址結構體,如 struct sockaddr_in(用于 IPv4),并將其地址強制轉換為 struct sockaddr *

struct sockaddr_in 結構體示例:

struct sockaddr_in {sa_family_t    sin_family; // 地址族,通常為 AF_INETin_port_t      sin_port;   // 服務器端口號,網絡字節序struct in_addr sin_addr;   // 服務器IP地址,網絡字節序
};

關鍵點: 在填充這個結構體時,必須使用網絡字節序。你需要將服務器的端口號和 IP 地址從主機字節序轉換過來,通常使用 htons()inet_addr()/inet_pton() 函數。

3. addrlen(地址結構體長度)

這個參數指定了 addr 指向的地址結構體的實際大小。通常使用 sizeof(struct sockaddr_in)


connect() 的作用和流程

當客戶端調用 connect() 函數時,會觸發以下一系列事件:

  1. 發送 SYN 包connect() 函數內部,操作系統會向服務器端的 IP 地址和端口號發送一個 TCP SYN(同步)報文,發起三次握手。
  2. 等待 SYN-ACKconnect() 函數會阻塞,等待服務器端的 SYN-ACK(同步-確認)報文。
  3. 發送 ACK 包:當收到 SYN-ACK 后,客戶端操作系統會發送一個 ACK(確認)報文給服務器。
  4. 連接建立:三次握手完成。此時,connect() 函數返回 0,表示連接已成功建立。

如果 connect() 失敗了,通常會有以下原因:

  • ECONNREFUSED:服務器端沒有監聽該端口(即沒有調用 listen()),或者該端口上有防火墻阻止連接。
  • ETIMEDOUT:連接超時。服務器可能因為網絡問題無法到達,或者服務器沒有回應。
  • ENETUNREACH:無法到達網絡。
  • EADDRINUSE:客戶端的本地地址或端口已被使用。

客戶端的 bind()connect()

一個有趣的細節是,客戶端在調用 connect() 之前,通常不需要調用 bind()

  • 如果未調用 bind():操作系統會在 connect() 內部自動為客戶端套接字分配一個可用的臨時(匿名)端口號和本地 IP 地址。
  • 如果調用了 bind():客戶端可以指定一個特定的本地 IP 地址和端口號來發起連接。這在某些特殊應用場景下可能有用,但大部分情況下并不需要。

? 客戶端經典的通信流程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define SERVER_IP "127.0.0.1" // 服務器IP地址
#define PORT 8080             // 服務器端口號int main() {int client_fd;struct sockaddr_in server_addr;// 1. 創建套接字if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket creation error");exit(EXIT_FAILURE);}// 2. 準備服務器地址結構體server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);// 將IP地址字符串轉換為網絡字節序if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {perror("invalid address/address not supported");close(client_fd);exit(EXIT_FAILURE);}// 3. 連接到服務器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("connection failed");close(client_fd);exit(EXIT_FAILURE);}printf("Successfully connected to server at %s:%d\n", SERVER_IP, PORT);// 4. 在這里進行數據通信(send/recv)...const char *message = "Hello from client";send(client_fd, message, strlen(message), 0);printf("Message sent to server\n");// 5. 關閉套接字close(client_fd);return 0;
}

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

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

相關文章

【Mermaid 離線工具】Mermaid 流程圖生成器 - 高清PNG輸出,一鍵生成專業級流程圖!

文章目錄 Mermaid 流程圖生成器(離線版本):高效繪圖,離線也能玩轉專業可視化 一、Mermaid:文本繪圖的 “魔法語法” 二、離線版生成器:功能與優勢解析 (一)離線可用,場景更靈活 (二)操作流程:簡單五步,產出專業圖表 (三)界面設計:簡潔直觀,降低使用門檻 三、應…

haproxy原理及實戰部署

一、負載均衡 負載均衡是網絡架構和分布式系統中至關重要的技術&#xff0c;其核心作用是將大量的并發請求或數據流量合理分配到多個服務器&#xff08;或其他資源節點&#xff09;上&#xff0c;從而解決單節點壓力過大、資源利用率低、系統穩定性差等問題。 作用1. 提高系統吞…

jwt 在net9.0中做身份認證

一、新建net9.0項目WebApplication1&#xff0c;安裝包 <ItemGroup><PackageReference Include"Microsoft.AspNetCore.Authentication.JwtBearer" Version"9.0.7" /><PackageReference Include"Swashbuckle.AspNetCore" Version&…

【機器學習深度學習】微調能改變模型“智商”嗎?——模型能力與知識的本質解析

目錄 前言 一、模型的“知識”與“能力”&#xff1a;兩種不同的智能 第一種&#xff1a;淺層知識&#xff08;記憶 模式識別&#xff09; 第二種&#xff1a;深層能力&#xff08;推理 理解&#xff09; 二、微調&#xff1a;改變的是“經歷”&#xff0c;不是“天賦”…

oracle數據庫表空間碎片整理

oracle數據庫表空間碎片整理 表空間碎片情況檢查 表空間碎片問題處理 收縮表 表空間手動整理 exp/imp導出再導入 移動表到新的表空間 表空間碎片情況檢查 對比表實際使用空間和數據文件占用空間: --實際數據占用空間 select tablespace_name,round(sum(bytes/1024/1024/1024…

為什么需要可重入鎖

在黑馬點評項目實戰中&#xff0c;提到了可重入鎖&#xff0c;然后我想到了是不是不同業務在同一線程內反復獲取同一把鎖。本文來討論一下為什么鎖需要可重入。一、可重入鎖的核心&#xff1a;“同一線程多次獲取同一把鎖”??可重入&#xff08;Reentrant&#xff09;?? 的…

【AI】聯網模式

【AI】聯網模式 文章目錄【AI】聯網模式1. 簡介2. 接入步驟2.1 引入依賴2.2 方法構建2.3 接口構建1. 簡介 在使用聯網模式之前&#xff0c;我們如果問起ai一些最近網絡上流傳的一些東西&#xff0c;它可能并不能準確的給你描述出來&#xff0c;因為它的知識庫更新時間可能停留…

第10篇:實戰驗收篇

&#x1f50d; 實戰演練&#xff1a;多條件房源查詢 需求描述 查找一套符合以下條件的房子&#xff1a; 預算&#xff1a;2000–3000元區域&#xff1a;天河區戶型&#xff1a;兩房 關鍵詞&#xff1a;多條件查詢 AND BETWEEN LIKE 組合運用&#x1f3ac; 開場白“聽起來不難&a…

深入解析YARN中的FairScheduler與CapacityScheduler:資源分配策略的核心區別

YARN資源調度器概述在Hadoop生態系統中&#xff0c;YARN&#xff08;Yet Another Resource Negotiator&#xff09;作為核心資源管理平臺&#xff0c;其架構設計將計算資源管理與作業調度解耦&#xff0c;形成了"全局資源管理器&#xff08;ResourceManager&#xff09;節…

基于Seata的微服務分布式事務實戰經驗分享

基于Seata的微服務分布式事務實戰經驗分享 1. 業務場景描述 在電商系統中&#xff0c;用戶下單會涉及多個微服務&#xff1a;訂單服務&#xff08;Order Service&#xff09;、庫存服務&#xff08;Inventory Service&#xff09;、賬戶服務&#xff08;Account Service&#x…

Linux庫——庫的制作和原理(2)_庫的原理

文章目錄庫的原理理解目標文件ELF文件讀取ELF的工具——readelfELF從形成到加載的輪廓ELF形成可執行文件ELF可執行的加載理解鏈接與加載靜態鏈接ELF加載和進程地址空間虛擬地址 & 邏輯地址重新理解進程地址空間動態鏈接和動態庫的加載進程如何找到動態庫多個進程之間如何共…

Redis C++客戶端——通用命令

目錄 代碼案例 get和set部分 exists部分 del部分 keys部分 expire部分 type部分 本篇文章主要是通過redis-plus-plus庫使用通用命令。 代碼案例 下面用一個代碼演示&#xff1a; #include <sw/redis/redis.h> #include <iostream> #include <vecto…

手機開啟16k Page Size

我買了一個pixel8的手機&#xff0c;系統是Android16,如下操作都是基于這個手機做的。 https://source.android.com/docs/core/architecture/16kb-page-size/16kb-developer-option?hlzh-cn#use_16kb_toggle 使用 16 KB 切換開關 按照開發者選項文檔中的指示啟用開發者選項。…

VLAN的劃分(基于華為eNSP)

VLAN的劃分 前言&#xff1a;為什么VLAN是現代網絡的“隱形骨架”&#xff1f; 當一臺辦公室電腦發送文件給隔壁工位的同事時&#xff0c;數據如何精準抵達目標而不“打擾”其他設備&#xff1f;當企業財務部的敏感數據在網絡中傳輸時&#xff0c;如何避免被其他部門的設備“窺…

從壓縮到加水印,如何實現一站式圖片處理

當你需要對大量圖片進行相同或相似的操作時&#xff08;例如壓縮、裁剪、調整尺寸、添加水印等&#xff09;&#xff0c;逐個處理會非常耗時。批量處理工具可以一次性處理數百張圖片&#xff0c;大大節省了時間。這是一款極致輕巧的圖片處理利器&#xff0c;體積僅有652KB&…

Pythong高級入門Day5

二、面向對象編程面向對象編程&#xff08;Object-Oriented Programming&#xff0c;簡稱OOP&#xff09;是一種通過組織對象來設計程序的編程方法。Python天生就是面向對象的模塊化編程。1. 初識類和對象示意圖&#xff1a;/-------> BYD E6(京A.88888) 實例&#xff0c;對…

C#其他知識點

接口類---interface什么是接口? 在接口當中一般我們認為接口中的成員都是抽象的。接口一般認為是功能的集合。在接口類當中定義的方法都是抽象象方法。(沒有方法體)接口一般我們認為它是一種標準,一種規范,一種約定。給子類或者是派生類制定規范,規定,標準。當子類繼承了該接口…

Maven 環境配置全攻略:從入門到實戰

一、Maven 簡介 Maven 是一個基于項目對象模型 (POM) 的項目管理工具&#xff0c;它可以通過一小段描述信息來管理項目的構建、報告和文檔。 除了強大的程序構建能力外&#xff0c;Maven 還提供了高級項目管理功能。其默認構建規則具有很高的可重用性&#xff0c;通常只需兩三…

現代 C++ 開發工作流(VSCode / Cursor)

? 推薦的現代 C 開發工作流&#xff08;含 VSCode / Cursor 插件配置&#xff09;&#x1f9f0; 一、環境要求 C 編譯器&#xff08;如 g 或 clang&#xff09;CMake&#xff08;建議 ≥ 3.16&#xff09;clangd&#xff08;建議 ≥ 14&#xff0c;最好用系統包管理器安裝&…

[SAP ABAP] ALV報表練習4

SO銷售訂單明細報表業務目的&#xff1a;根據選擇屏幕的篩選條件&#xff0c;使用ALV報表顯示銷售訂單詳情(Sales Order、Material、現有Qty、已開立數量以及剩余數量等)信息效果展示我們在銷售訂單欄位輸入需要查詢的SO單號&#xff0c;這里我們以SO單號0000000221為例&#x…