| 用法 | 修飾對象 | 核心影響 | 本質 |
|---|---|---|---|
| 靜態局部變量 | 函數內變量 | 生存期:程序全生命周期;存儲:靜態區;作用域:函數內;默認初始化 0 | 改變存儲類別 |
| 靜態全局變量 | 文件域變量 | 作用域:僅本編譯單元可見 (內部連接);存儲:靜態區 | 改變連接屬性 |
| 靜態函數 | 文件域函數 | 作用域:僅本編譯單元可見 (內部連接) | 改變連接屬性 |
extern int g_val; 告訴編譯器「g_val 在別處定義,鏈接時解析」,不分配內存。int g_val = 10;,B.c 包含頭文件 extern int g_val; 即可讀寫同一內存。| 維度 | static (內部連接) | extern (外部連接) |
|---|---|---|
| 符號可見性 | 僅本目標文件 (.o/.obj) | 全局可見,參與鏈接解析 |
| 多文件同名 | 各文件獨立拷貝,互不干擾 | 鏈接報錯「重複定義」 |
| 頭文件定義 | ❌ 極其危險:每個 .c 產生獨立拷貝,邏輯斷裂 | ✅ 標準做法:頭文件 extern 聲明,.c 定義 |
| 符號表表現 | 小寫字母 d/b/t (nm 輸出) | 大寫字母 D/B/T |
// header.h
static int g_counter = 0; // 每個包含此 .c 都有獨立 g_counter!
// a.c #include "header.h" void inc_a() { g_counter++; }
// b.c #include "header.h" void inc_b() { g_counter++; }
// main.c 呼叫 inc_a(), inc_b() → 兩個獨立計數器,非預期共享!
// 編譯器優化可能合併相同常量內容,但修改時會強制分離 (COW),極難調試。
鐵律:**頭文件只放 extern 聲明,定義放 .c;文件內部私有變量/函數才加 static 定義在 .c 中。**
const 全局默認內部連接 (等同 static)。extern const int MAX_CONN = 100;,**必須在某 .c 中定義** const int MAX_CONN = 100; (C++ 允許頭文件定義並初始化,但為統一風格建議分離)。const char* p = "str"; → p 可變,*p 只讀。跨文件共享只讀指針需 extern const char* const p; 定義 const char* const p = "str";。| 步驟 | 發送方 | 報文標誌 | 序列號/確認號 | 狀態變遷 |
|---|---|---|---|---|
| 1 | 客戶端 A | SYN (syn=1) | SEQ=j | CLOSED → SYN_SENT |
| 2 | 服務端 B | SYN+ACK (syn=1, ack=1) | SEQ=k, ACK=j+1 | LISTEN → SYN_RCVD |
| 3 | 客戶端 A | ACK (ack=1) | ACK=k+1 | SYN_SENT → ESTABLISHED SYN_RCVD → ESTABLISHED |
**作用**:雙向確認對方發送/接收能力正常,同步初始序列號 (ISN),分配 TCB (傳輸控制塊)。
| 步驟 | 發送方 | 報文標誌 | 序列號/確認號 | 狀態變遷 |
|---|---|---|---|---|
| 1 | 主動關閉方 A | FIN | SEQ=u | ESTABLISHED → FIN_WAIT_1 |
| 2 | 被動方 B | ACK | ACK=u+1 | ESTABLISHED → CLOSE_WAIT |
| 3 | 被動方 B | FIN | SEQ=v | CLOSE_WAIT → LAST_ACK |
| 4 | 主動方 A | ACK | ACK=v+1 | FIN_WAIT_2 → TIME_WAIT → (2MSL) → CLOSED LAST_ACK → CLOSED |
**核心**:全雙工獨立關閉,FIN 佔用 1 個序列號,TIME_WAIT 保證最後 ACK 可靠送達並讓舊重複分組消亡。
| 狀態 | 含義 | 典型觸發場景 |
|---|---|---|
| CLOSED | 初始/最終狀態,無連接 | socket 創建、關閉完成 |
| LISTEN | 服務端監聽,接受連接 | bind+listen 後 |
| SYN_SENT | 客戶端已發 SYN,等待 SYN+ACK | connect() 後 |
| SYN_RCVD | 服務端收到 SYN,已回 SYN+ACK,等待 ACK | 三次握手中間態,極短暫 |
| ESTABLISHED | 連接建立,雙向數據傳輸 | 握手完成、正常通信 |
| FIN_WAIT_1 | 主動發 FIN,等待 ACK | close()/shutdown(SHUT_WR) |
| FIN_WAIT_2 | 收到 ACK,半關閉,等待對方 FIN | 對方確認關閉,但仍有數據發送 |
| TIME_WAIT | 收到 FIN,已回 ACK,等 2MSL 確保可靠關閉 | 主動關閉方最後階段,防舊包干擾 |
| CLOSE_WAIT | 被動收到 FIN,已回 ACK,**應用層需決定何時 close** | 被動關閉方,業務邏輯處理中 |
| LAST_ACK | 被動發 FIN,等待最後 ACK | 應用層調用 close() 後 |
| CLOSING | 雙方近乎同時發 FIN,罕見 | 雙方同步 close() |
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
# 輸出示例:
# LAST_ACK 5
# SYN_RECV 30
# ESTABLISHED 15
# FIN_WAIT1 51
# FIN_WAIT2 5
# TIME_WAIT 10
net.ipv4.tcp_tw_reuse=1、tcp_max_tw_buckets、啟用長連接/連接池。本文將 **C 語言連接屬性關鍵字 (static/extern/const)** 與 **TCP 協議狀態機 (三次握手/四次揮手/11狀態/netstat 診斷)** 兩大看似無關實為「系統程序員必修雙基」的知識體系整合呈現。前者決定符號在編譯鏈接階段的可見性與生命週期,是模塊化、封裝、跨語言互操作的基石;後者決定網絡連接在運行時的建立、傳輸、關閉全生命週期,是高性能網絡服務穩定性的生命線。**深度掌握雙基,方能在系統編程戰場上游刃有餘。**