網路與 IP

TCP 全稱是什麼?傳輸控制協議的特點、首部與工作過程

TCP 的全稱是 Transmission Control Protocol,中文叫傳輸控制協議,是 TCP/IP 協議簇裡負責可靠傳輸的傳輸層協議。它先建立連線,再用序號、確認和重傳保證資料不丟、不亂、不重,並用流量控制和擁塞控制調節傳送速度;網頁、SSH、遠端桌面和資料庫都跑在它上面。

作者 頂聯產品團隊發布於 9 分鐘閱讀

TCP 全稱是 Transmission Control Protocol,中文譯為傳輸控制協議。它是 TCP/IP 協議簇中工作在傳輸層的協議,封裝在 IP 包裡,IPv4 頭部用協議號 6 標識它。TCP 最早在 1981 年由 RFC 793 標準化,2022 年釋出的 RFC 9293 把此後幾十年的修訂合併成新的標準文字,取代了 RFC 793。用一句話回答“TCP 是什麼協議”:它在兩台主機的兩個程式之間,提供面向連線、可靠、按序的位元組流傳輸。伺服器上的網站(HTTP、HTTPS)、SSH、遠端桌面、MySQL 這類資料庫,都依賴 TCP 把資料完整地送到對端。下面依次講它的特點、首部欄位、一次連線的完整過程、流量控制與擁塞控制,以及伺服器維運中最常碰到的 TCP 報錯。

TCP 是什麼協議:在 IP 之上補上“可靠”

IP 只負責“盡力而為”地把包送到目標主機:路上可能丟包,可能因為走了不同路徑而亂序,可能因為重發而重複,也可能在傳輸中出錯。應用程式如果直接面對這些問題,每個程式都得自己寫一遍重傳和排序。TCP 的作用就是在作業系統核心裡統一處理掉這些問題,讓程式只需要“寫進去、讀出來”。

IP 層可能發生的情況 TCP 用什麼機制解決
包丟了 接收方對收到的資料發確認,傳送方超時沒收到確認就重傳
包亂序到達 每個位元組都有序號,接收方按序號重新排好再交給程式
包重複到達 按序號識別出已經收過的資料,直接丟棄
內容在傳輸中出錯 校驗和不對的報文段直接丟棄,靠重傳補上
接收方處理不過來 流量控制:接收方在每個報文裡通告自己還能收多少
網路本身擁堵 擁塞控制:傳送方根據丟包和延遲主動降速

有兩件事 TCP 不管:一是不加密,資料線上路上是明文,HTTPS、SSH 的加密由 TLS 或 SSH 協議自己完成;二是不保證時效,它保證資料最終完整到達,但丟包重傳時,後面的資料要等前面的補齊才能交給程式,延遲會上升。

TCP 只存在於通訊的兩端。路由器和二層交換器轉發時只看 IP 和 MAC,不維護 TCP 狀態;防火牆、負載均衡、NAT 裝置會讀取 TCP 頭部,按連線狀態做判斷,這也是它們能攔截或打斷 TCP 連線的原因。

TCP 協議的特點:五個關鍵詞分別指什麼

特點 具體含義 帶來的代價或注意點
面向連線 傳資料之前先握手,雙方交換初始序號,告知各自的最大報文段長度(MSS),並協商視窗縮放、SACK 等選項;連線由源 IP、源連接埠、目的 IP、目的連接埠四元組唯一確定 多一個往返才能開始傳資料;每條連線在兩端都佔用記憶體和狀態
可靠傳輸 序號按位元組編號,確認號表示“下一個期望收到的位元組”;超時未確認就重傳,收到 3 個重複確認會提前重傳(快速重傳) 丟包時延遲上升;Linux 的重傳超時最小約 200 毫秒
位元組流 程式寫入的資料被看作連續的位元組,按 MSS 切分傳送,不保留每次寫入的邊界 應用層要自己劃分訊息,也就是常說的“粘包”問題
全雙工 兩個方向各有獨立的序號和視窗,可以同時收發;一方關閉傳送後,仍可繼續接收(半關閉) 關閉連線時兩個方向要分別關閉
流量與擁塞控制 傳送量同時受接收方視窗和傳送方估算的擁塞視窗限制 長距離、有丟包的線路上,單條連線往往跑不滿頻寬

另外,TCP 只支援一對一通訊。廣播、組播這類一對多的場景用不了 TCP,只能用 UDP。

“面向連線”中的四元組值得多說一句:伺服器上的 Nginx 只監聽一個 443 連接埠,卻能同時服務幾萬個客戶端,因為每個客戶端的 IP 和源連接埠不同,四元組各不相同,核心據此把報文分發到各自的連線上。

TCP 首部格式:20 位元組固定欄位

TCP 首部固定部分 20 位元組,後面可以跟最多 40 位元組的選項,所以首部長度在 20 到 60 位元組之間。按每行 4 位元組排列:

位元組 0–3    | 源連接埠(16 位)                | 目的連接埠(16 位)               |
位元組 4–7    | 序號(32 位)                                                   |
位元組 8–11   | 確認號(32 位)                                                 |
位元組 12–15  | 首部長度(4 位)| 保留(4 位)| 標誌位(8 位)| 視窗(16 位)       |
位元組 16–19  | 校驗和(16 位)                | 緊急指標(16 位)               |
位元組 20 起  | 選項(0–40 位元組,長度為 4 的倍數)                              |
欄位 長度 作用 維運中在哪裡見到
源連接埠 16 位 傳送方的連接埠,客戶端一般由系統從臨時連接埠範圍裡分配 Linux 預設臨時連接埠範圍 32768–60999,sysctl net.ipv4.ip_local_port_range 檢視
目的連接埠 16 位 接收方程式監聽的連接埠 防火牆規則裡的 --dport 443
序號 32 位 本報文段第一個資料位元組的編號;SYN 報文裡是隨機生成的初始序號 tcpdump 輸出的 seq
確認號 32 位 期望收到對方的下一個位元組編號,ACK 標誌置位時有效 tcpdump 輸出的 ack
首部長度 4 位 以 4 位元組為單位,取值 5–15,對應 20–60 位元組 抓包工具裡的 Header Length
保留 4 位 置 0,留作擴充套件 —
標誌位 8 位 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN tcpdump 的 Flags [S.]
視窗 16 位 接收方還能接收多少位元組,需乘以握手時協商的縮放因子 win 值長時間為 0 表示接收方處理不過來
校驗和 16 位 覆蓋偽首部(源 IP、目的 IP、協議號、長度)、首部和資料 本機發出的包在抓包裡顯示校驗和錯誤,多半是網路卡校驗和解除安裝,不是故障
緊急指標 16 位 URG 置位時指明緊急資料的位置,現在幾乎不用 —

八個標誌位裡,日常排障用得最多的是這幾個,括號裡是 tcpdump 的顯示符號:SYN(S)發起連線、ACK(.)確認、FIN(F)正常關閉、RST(R)強制復位、PSH(P)提示對方儘快把資料交給程式。ECE(E)和 CWR(W)用於顯式擁塞通知(ECN),URG(U)用於緊急資料。

常見選項都只在握手階段協商一次:MSS 告訴對方自己能接收的最大資料段,乙太網 MTU 1500 時通常是 1460 位元組;視窗縮放把 16 位視窗左移最多 14 位,使視窗上限從 65535 位元組擴大到約 1 GB;SACK 允許接收方告訴傳送方“哪幾段已經收到”,丟包時只重傳缺的部分;時間戳用於更準確地測量往返時間,並防止序號迴繞帶來的混淆。

一次 TCP 連線的完整過程:從握手到揮手

在伺服器 198.51.100.23 上執行 curl http://203.0.113.10/,另開一個視窗抓包:

tcpdump -nn -i eth0 host 203.0.113.10 and tcp port 80

整個連線大致由下面這些報文組成。為了便於閱讀,雙方的初始序號都記為 0、後面用相對值表示(tcpdump 在握手報文裡顯示真實的隨機序號,之後才換算成相對值),資料大小為範例:

序 方向 標誌 序號與確認號 內容 階段
1 客戶端 → 伺服器 S seq 0 攜帶 MSS 1460、SACK、視窗縮放等選項 握手
2 伺服器 → 客戶端 S. seq 0,ack 1 伺服器的初始序號和選項 握手
3 客戶端 → 伺服器 . ack 1 連線建立 握手
4 客戶端 → 伺服器 P. seq 1:79 78 位元組的 HTTP 請求 傳資料
5 伺服器 → 客戶端 . ack 79 確認收到請求 傳資料
6 伺服器 → 客戶端 . seq 1:1449 第一段回應,1448 位元組 傳資料
7 伺服器 → 客戶端 P. seq 1449:2897 第二段回應,1448 位元組 傳資料
8 客戶端 → 伺服器 . ack 2897 一次確認兩段資料 傳資料
9 客戶端 → 伺服器 F. seq 79 curl 收完回應,主動關閉 揮手
10 伺服器 → 客戶端 F. ack 80 確認對方的 FIN,同時發出自己的 FIN 揮手
11 客戶端 → 伺服器 . ack 2898 確認伺服器的 FIN 揮手

從這張表能讀出 TCP 的幾個關鍵行為:

  • 握手只做協商,不傳業務資料。前三個報文的長度都是 0,真正的請求從第 4 個報文才開始。SYN 和 FIN 各佔用一個序號,所以第 3 個報文確認的是 1,第 10 個報文確認的是 80。
  • 確認是累計的。第 8 個報文的 ack 2897 一次確認了第 6、7 兩段,不必逐段確認;Linux 還會稍微延遲傳送確認,等著和要發的資料合併。
  • 資料段大小是 1448 而不是 1460。雙方啟用了時間戳選項,每個報文段要拿出 12 位元組放選項,MSS 1460 減去 12 就是 1448。
  • 揮手常常只有三個包。理論上關閉連線要四個報文,兩個方向各一對 FIN 和 ACK;伺服器收到 FIN 時如果程式也馬上關閉,核心就把 ACK 和自己的 FIN 合併成一個報文發出。
  • 先關閉的一方進入 TIME_WAIT。這裡是 curl 先發 FIN,所以 TIME_WAIT 留在客戶端,Linux 上持續 60 秒,用 ss -tan state time-wait 能看到。

流量控制和擁塞控制有什麼區別

兩者都是“控制傳送速度”,但保護的物件不同:

對比項 流量控制 擁塞控制
保護誰 接收方,防止它的緩衝區被灌滿 網路,防止路徑上的鏈路和裝置過載
依據 接收方在每個報文的視窗欄位裡通告的接收視窗(rwnd) 傳送方自己估算的擁塞視窗(cwnd),不寫在報文裡
訊號來源 接收緩衝區還剩多少空間 丟包、往返時間變長、ECN 標記
調節方式 接收方調大或調小通告視窗,處理不過來時通告 0 慢啟動、擁塞避免、遇到丟包時降低視窗
常見問題 視窗太小,長距離傳輸跑不滿頻寬 線路有丟包,視窗一再被壓低

傳送方任何時刻在途(已發出但未確認)的資料量,不超過 rwnd 和 cwnd 中較小的那個。由此有一個很實用的估算:單條連線的速度上限約等於視窗除以往返時間。

  • 不啟用視窗縮放時,視窗最大 65535 位元組。往返時間 150 毫秒的跨境線路上,上限約為 65535 × 8 ÷ 0.15 ≈ 3.5 Mbit/s,頻寬再大也用不上。
  • 反過來,要在往返時間 150 毫秒的線路上跑滿 100 Mbit/s,視窗至少要 100,000,000 × 0.15 ÷ 8 ≈ 1.9 MB。

擁塞視窗從小開始增長。Linux 的初始擁塞視窗是 10 個報文段,約 14 KB;慢啟動階段每經過一個往返大約翻一倍。假設往返時間 30 毫秒、頻寬 100 Mbit/s,需要的視窗約 375 KB,即 259 個 1448 位元組的報文段,從 10 翻倍到這個量級要 5 個往返左右。這解釋了一個常見現象:下載幾十 KB 的小檔案時,連線還沒走完慢啟動就結束了,測出的速度遠低於頻寬,這不是線路問題。

遇到丟包時,擁塞控制會把視窗壓低:經典的 Reno 演算法減半,Linux 預設的 CUBIC 降到原來的約 70%,之後再逐步恢復。BBR 則主要依據測得的頻寬和往返時間來定速,不把丟包作為主要訊號。在伺服器上檢視和觀察:

sysctl net.ipv4.tcp_congestion_control              # 當前演算法,多數發行版預設 cubic
sysctl net.ipv4.tcp_available_congestion_control    # 核心已載入、可以切換的演算法
ss -tin state established '( sport = :443 )'        # 檢視本機 443 連接埠上每條連線的內部引數

ss 的 -i 輸出裡,cwnd:10 是當前擁塞視窗(單位是報文段),rtt:3.5/1.2 是平滑往返時間和波動(毫秒),retrans:0/3 是當前未恢復的和累計的重傳次數,wscale:7,7 是雙方的視窗縮放因子。某個客戶反映“下載慢”時,看他那條連線的 rtt 和 retrans,就能初步判斷是距離遠、線路丟包,還是伺服器自身的問題。

伺服器上哪些服務用 TCP,連線報錯對應什麼

服務 預設連接埠 為什麼用 TCP
HTTP / HTTPS 80 / 443 網頁、介面的內容一個位元組都不能錯;HTTP/3 改用 UDP 443 上的 QUIC 是例外
SSH、SFTP 22 命令和檔案必須完整、按序到達
遠端桌面 RDP 3389 認證和會話建立必須走 TCP,畫面傳輸可以額外用 UDP
MySQL / PostgreSQL / Redis 3306 / 5432 / 6379 查詢和結果不能丟失或亂序
SMTP、IMAP 25、465、587 / 143、993 郵件投遞要求完整送達
FTP 21,另有資料連接埠 控制連線和資料連線都是 TCP
BGP 179 路由協議借用 TCP 的可靠性,自己不再實現重傳
DNS 區域傳送和大應答 53 普通查詢走 UDP,資料量大時改用 TCP

正因為幾乎所有核心服務都依賴 TCP,伺服器上很多“連不上”“斷開了”的問題,本質是 TCP 層發生了某個具體事件。程式報出的錯誤資訊可以直接對應到報文:

程式報錯 TCP 層發生了什麼 常見原因
Connection refused 發出 SYN 後立即收到 RST(或 ICMP 連接埠不可達) 連接埠上沒有程式監聽;或防火牆用 REJECT 拒絕,回了 TCP 復位或 ICMP 連接埠不可達
Connection timed out SYN 重傳多次都沒有任何應答 安全組或防火牆靜默丟棄;主機不線上;回程路由有問題
Connection reset by peer 連線建立後收到 RST 對端程式崩潰或強制關閉連線;中間裝置的會話已超時被刪除
Broken pipe 向已經被對端關閉或復位的連線寫資料 對端已經關閉;長連線空閒太久被中間裝置斷開
No route to host 收到 ICMP 主機不可達 路由缺失;或防火牆以 ICMP 方式拒絕,firewalld 預設的拒絕就是這種

最後一類問題在機房裡很常見:空閒連線被中間裝置悄悄斷開。狀態防火牆和 NAT 裝置會刪除一段時間沒有報文的連線記錄,而 Linux 的 TCP 保活(keepalive)預設要空閒 7200 秒才開始探測,遠長於很多裝置的超時時間。SSH 空閒一會兒就卡死、資料庫連線池裡的連線用的時候才發現已失效,都是這個原因。處理辦法是讓應用層更頻繁地發保活:SSH 客戶端在 ~/.ssh/config 裡設定 ServerAliveInterval 60,或在伺服器的 sshd_config 裡設定 ClientAliveInterval 60;資料庫連線池開啟空閒檢測。

在管理系統裡怎麼落地

伺服器的 SSH 和遠端桌面都建立在它自己的 TCP 協議棧上:網路卡配置寫錯、防火牆把 22 或 3389 擋住、系統卡死,TCP 連線就建立不起來,再多的重試也沒用。Toplink DCIM 的 IPMI 遠端管理 在瀏覽器裡同時提供 SSH、RDP 和 VNC 控制檯:前兩者走伺服器作業系統的通道,日常使用;VNC 經由 BMC 連線,不依賴伺服器系統裡的網路和 TCP 服務,系統層面的連線出問題時,仍然能看到畫面進去修復。網路裝置一側也離不開 TCP:交換器管理 預設透過 SSH 或 Telnet 下發連接埠、VLAN 等配置,這兩種都是 TCP 上的會話,每一條命令都要可靠送達;SNMP 只用於唯讀採集。

常見問題

TCP 連接埠和 UDP 連接埠是同一套嗎?

不是。TCP 和 UDP 各有一套 0–65535 的連接埠號,TCP 53 和 UDP 53 是兩個獨立的連接埠,同一個連接埠號可以分別被兩個程式佔用。防火牆和安全組放行時要按協議分別寫規則。

TCP 粘包是什麼意思?

TCP 是位元組流,不保留訊息邊界:程式連續寫入的兩條訊息,對端可能一次讀到,也可能一條訊息分兩次才讀完。這不是故障,而是應用層要自己定邊界,常用固定長度、長度字首或分隔符三種辦法。

TCP 一定比 UDP 慢嗎?

不一定。TCP 建連多一個往返,丟包時要等重傳;但在穩定的線路上傳大檔案,TCP 同樣能跑滿頻寬。即時音影片、遊戲這類寧可丟資料也不願等重傳的業務,才更適合 UDP。

想看看在你的機房裡怎麼用?

從一次產品示範開始,梳理你的業務鏈路。

查看價格
服務熱線 400-112-2951

讓我們聊聊你的 IDC 業務

掃碼新增企業微信,預約產品示範或申請試用。

Toplink 企業微信二維碼

長按儲存或使用企業微信 / 微信掃描

也可致電
400-112-2951
Telegram
@TopLink88