《UNIX網絡編程卷1:套接字聯網API》第2章 傳輸層:TCP、UDP和SCTP
2.1 傳輸層的核心作用與協議選型
傳輸層是網絡協議棧中承上啟下的核心層,直接決定應用的通信質量。其主要職責包括:
- 端到端通信:屏蔽底層網絡細節,為應用提供邏輯通信通道;
- 可靠性保障(TCP/SCTP):通過確認、重傳、擁塞控制等機制確保數據完整;
- 多路復用與分用:通過端口號區分不同應用進程;
- 流量控制:防止發送方壓垮接收方緩沖區。
協議選型黃金法則:
- TCP:需要可靠傳輸、數據順序性的場景(如文件傳輸、HTTP);
- UDP:低延遲、容忍丟包、強調實時性的場景(如視頻會議、DNS查詢);
- SCTP:多宿主容災、多流并發、消息邊界保留的場景(如5G核心網信令)。
2.2 TCP協議深度解析
2.2.1 TCP頭部結構與字段詳解
TCP頭部由20字節固定部分和可變選項組成,其結構如下(圖2-1):
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (可變長度) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
關鍵字段解析:
- 序列號(Sequence Number):32位,標識數據段的第一個字節的編號;
- 確認號(Acknowledgment Number):32位,期望收到的下一個字節的編號;
- 窗口大小(Window):16位,接收方的可用緩沖區大小(流量控制核心);
- 標志位:
SYN
:發起連接;ACK
:確認數據;FIN
:關閉連接;RST
:強制重置連接;URG
:緊急指針有效。
2.2.2 TCP連接管理:三次握手與四次揮手
-
三次握手建立連接(圖2-2):
客戶端 服務器|-------- SYN(seq=100) ------->||<-- SYN+ACK(seq=300, ack=101)--||-------- ACK(ack=301) -------->|
- 序列號隨機化:防止歷史報文干擾(RFC 6528);
- 半連接隊列:服務器在SYN_RCVD狀態維護未完成握手隊列。
-
四次揮手終止連接(圖2-3):
主動關閉方 被動關閉方|-------- FIN(seq=200) ------->||<-------- ACK(ack=201)--------||<-------- FIN(seq=500)--------||-------- ACK(ack=501) ------->|
- TIME_WAIT狀態:主動關閉方等待2MSL(最大報文段生存時間),防止舊報文干擾新連接;
- 孤兒連接:被動關閉方在CLOSE_WAIT狀態需及時關閉。
代碼示例:TCP服務器監聽連接
int listenfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ); // LISTENQ通常設置為SOMAXCONN
2.2.3 流量控制與擁塞控制
-
流量控制:
- 滑動窗口協議:接收方通過窗口字段通告可用緩沖區大小;
- 零窗口探測:發送方定時發送1字節數據,避免死鎖。
-
擁塞控制:
- 慢啟動(Slow Start):窗口指數增長至閾值;
- 擁塞避免(Congestion Avoidance):窗口線性增長;
- 快速重傳(Fast Retransmit):收到3個重復ACK立即重傳;
- 快速恢復(Fast Recovery):避免窗口重置為1。
內核參數調優(Linux示例):
# 調整接收緩沖區大小
sysctl -w net.ipv4.tcp_rmem='4096 87380 6291456'
# 啟用BBR擁塞控制算法
sysctl -w net.ipv4.tcp_congestion_control=bbr
2.3 UDP協議特性與編程實踐
2.3.1 UDP頭部結構
UDP頭部僅8字節(圖2-4):
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| 源端口 | 目的端口 |
+--------+--------+--------+--------+
| 長度 | 校驗和 |
+--------+--------+--------+--------+
| 數據(若有) |
+-----------------------------------+
核心特點:
- 無連接:無需握手,直接發送數據報;
- 無可靠性保證:不重傳、不排序、不擁塞控制;
- 數據報邊界保留:每個
sendto
對應一個完整報文。
2.3.2 UDP編程核心挑戰
- 報文丟失處理:需應用層實現超時重傳與確認機制;
- 亂序處理:為每個報文添加序列號;
- MTU限制:避免IP分片(通常限制為1472字節,1500-20-8)。
代碼示例:UDP時間服務器
int sockfd = Socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));char buff[MAXLINE];
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);for (;;) {int n = Recvfrom(sockfd, buff, MAXLINE, 0, (SA*)&cliaddr, &len);time_t ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));Sendto(sockfd, buff, strlen(buff), 0, (SA*)&cliaddr, len);
}
2.3.3 UDP的適用場景
- 實時音視頻傳輸(如WebRTC):容忍丟包,但要求低延遲;
- DNS查詢:單次請求響應,無連接開銷;
- 廣播/多播通信:向多個接收者高效發送數據。
2.4 SCTP協議:下一代傳輸協議
2.4.1 SCTP核心特性
- 多宿主(Multi-homing):一個端點可綁定多個IP地址,提升容災能力;
- 多流(Multi-streaming):獨立的數據流避免隊頭阻塞;
- 消息邊界保留:基于消息而非字節流;
- 四次握手關聯建立:抵御SYN洪泛攻擊。
2.4.2 SCTP關聯建立與關閉
-
四次握手建立關聯(圖2-5):
客戶端 服務器|-------- INIT(隨機數A) ------->||<------- INIT-ACK(隨機數B)-----||------- COOKIE-ECHO(加密Cookie)>||<------- COOKIE-ACK ------------|
- 狀態Cookie:服務器不保存狀態,防止DDoS攻擊。
-
優雅關閉關聯:
主動關閉方 被動關閉方|-------- SHUTDOWN -------------->|<------- SHUTDOWN-ACK -----------||<------- SHUTDOWN-COMPLETE ------|
代碼示例:SCTP單流服務器
int listenfd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));struct sctp_event_subscribe events;
bzero(&events, sizeof(events));
events.sctp_data_io_event = 1; // 啟用數據IO事件
Setsockopt(listenfd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events));Listen(listenfd, LISTENQ);
2.5 協議對比與選型指南
特性 | TCP | UDP | SCTP |
---|---|---|---|
連接方式 | 面向連接 | 無連接 | 面向關聯 |
可靠性 | 可靠 | 不可靠 | 可靠 |
數據邊界 | 無 | 有 | 有 |
傳輸模式 | 字節流 | 數據報 | 消息流 |
多路徑支持 | 否 | 否 | 是(多宿主) |
典型應用 | HTTP、FTP、SSH | DNS、QUIC、實時音視頻 | 5G信令、VoLTE |
選型建議:
- 嵌入式設備:優先UDP(資源占用低),復雜場景用TCP;
- 高可用服務:SCTP多宿主特性提升容災能力;
- 實時系統:UDP+應用層協議(如RTSP)。
2.6 實戰:多協議時間服務器
2.6.1 TCP時間服務器
(代碼參考第1章示例)
2.6.2 UDP時間服務器
#include "unp.h"
#include <time.h>int main() {int sockfd = Socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(9999);Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));char buff[MAXLINE];struct sockaddr_in cliaddr;socklen_t len;for (;;) {len = sizeof(cliaddr);Recvfrom(sockfd, buff, MAXLINE, 0, (SA*)&cliaddr, &len);time_t ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));Sendto(sockfd, buff, strlen(buff), 0, (SA*)&cliaddr, len);}
}
2.6.3 SCTP時間服務器
#include "unp.h"
#include <netinet/sctp.h>
#include <time.h>int main() {int sockfd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(9999);Bind(sockfd, (SA*)&servaddr, sizeof(servaddr));struct sctp_event_subscribe events;bzero(&events, sizeof(events));events.sctp_data_io_event = 1;Setsockopt(sockfd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events));Listen(sockfd, LISTENQ);struct sockaddr_in cliaddr;char buff[MAXLINE];for (;;) {socklen_t len = sizeof(cliaddr);int connfd = Accept(sockfd, (SA*)&cliaddr, &len);time_t ticks = time(NULL);snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));Write(connfd, buff, strlen(buff));Close(connfd);}
}
2.7 調試工具與性能分析
2.7.1 Wireshark抓包分析
- TCP流追蹤:右鍵報文 → Follow → TCP Stream;
- UDP過濾:
udp.port == 9999
; - SCTP關聯分析:
sctp.association_id
。
2.7.2 網絡性能測試
- TCP吞吐量測試:
# 服務器端 iperf3 -s # 客戶端 iperf3 -c 192.168.1.100 -t 30
- UDP丟包率測試:
iperf3 -u -c 192.168.1.100 -b 100M -t 20
2.8 本章小結與進階習題
小結:本章深入解析了TCP、UDP、SCTP的協議機制與編程實踐,通過對比分析指導協議選型,為復雜網絡應用開發奠定基礎。
習題:
- 實現SCTP多流客戶端,驗證不同流的獨立性;
- 使用UDP實現可靠文件傳輸協議(含ACK與超時重傳);
- 分析TCP BBR與CUBIC擁塞控制算法的差異,編寫測試報告。
付費用戶專屬資源:
- 完整多協議時間服務器代碼工程;
- Wireshark抓包文件(標注關鍵字段);
- 擴展閱讀:《SCTP在5G核心網中的實踐》。
通過本章學習,讀者將掌握傳輸層協議的核心原理,并能夠根據場景需求選擇最佳協議,設計高效可靠的網絡應用。