傳輸控制協議（，縮寫：）是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議，由 IETF 的定義。在簡化的計算機網路 OSI 模型中，它完成第四層傳輸層所指定的功能。使用者資料報協議（UDP）是同一層內另一個重要的傳輸協議。 在因特網協議族中，TCP 層是位於 IP 層之上，應用層之下的中間層。不同主機的應用層之間經常需要可靠的、像管道一樣的連線，但是 IP 層不提供這樣的流機制，而是提供不可靠的包交換。 應用層向 TCP 層傳送用於網間傳輸的、用 8 位位元組表示的資料流，然後 TCP 把資料流分割成適當長度的報文段（通常受該計算機連線的網路的資料鏈路層的最大傳輸單元（MTU）的限制）。之後 TCP 把結果包傳給 IP 層，由它來透過網路將包傳送給接收端實體的 TCP 層。TCP 為了保證不發生丟包，就給每個包一個序號，同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的包發回一個相應的確認資訊（ACK）；如果傳送端實體在合理的往返時延（RTT）內未收到確認，那麼對應的資料包就被假設為已丟失並進行重傳。TCP 用一個校驗和函式來檢驗資料是否有錯誤，在傳送和接收時都要計算校驗和。 簡介 資料在 TCP 層稱為流（Stream），資料分組稱為分段（Segment）。作為比較，資料在 IP 層稱為 Datagram，資料分組稱為分片（Fragment）。 UDP 中分組稱為 Message。 運作方式 TCP 協議的執行可劃分為三個階段：連線建立 connection establishment、資料傳送（data transfer）和連線終止（connection termination）。作業系統將 TCP 連線抽象為套接字表示的本地端點（local endpoint），作為程式設計介面給程式使用。在 TCP 連線的生命期內，本地端點要經歷一系列的狀態改變。RFC 793 Section 32 建立通路 TCP 用三 zhhans 次；zhhant 路 握手（或稱三 zhhans 路；zhhant 次 握手，）過程建立一個連線。在連線建立過程中，很多引數要被初始化，例如序號被初始化以保證按序傳輸和連線的強壯性。 一對終端同時初始化一個它們之間的連線是可能的。但通常是由一端（伺服器端）開啟一個套接字（socket）然後監聽來自另一方（客戶端）的連線，這就是通常所指的被動開啟（passive open）。伺服器端被被動開啟以後，客戶端就能開始建立主動開啟（active open）。 伺服器端執行了 listen 函式後，就在伺服器上建立起兩個佇列： SYN 佇列：存放完成了二次握手的結果。 佇列長度由 listen 函式的引數 backlog 指定。 ACCEPT 佇列：存放完成了三次握手的結果。佇列長度由 listen 函式的引數 backlog 指定。 三次握手協議的過程： 客戶端（透過執行 connect 函式）向伺服器端傳送一個 SYN 包，請求一個主動開啟。該包攜帶客戶端為這個連線請求而設定的隨機數 A 作為訊息序列號。 伺服器端收到一個合法的 SYN 包後，把該包放入 SYN 佇列中；回送一個 SYNACK。ACK 的確認碼應為 A1，SYNACK 包本身攜帶一個隨機產生的序號 B。 客戶端收到 SYNACK 包後，傳送一個 ACK 包，該包的序號被設定為 A1，而 ACK 的確認碼則為 B1。然後客戶端的 connect 函式成功返回。當伺服器端收到這個 ACK 包的時候，把請求幀從 SYN 佇列中移出，放至 ACCEPT 佇列中；這時 accept 函式如果處於阻塞狀態，可以被喚醒，從 ACCEPT 佇列中取出 ACK 包，重新建立一個新的用於雙向通訊的 sockfd，並返回。 如果伺服器端接到了客戶端發的 SYN 後回了 SYNACK 後客戶端 zhhans 掉；zhhant 斷 線了，伺服器端沒有收到客戶端回來的 ACK，那麼，這個連線處於一箇中間狀態，既沒成功，也沒失敗。於是，伺服器端如果在一定時間內沒有收到的 TCP 會重發 SYNACK。在 Linux 下，預設重試次數為 5 次，重試的間隔時間從 1s 開始每次都翻倍，5 次的重試時間間隔為 1s 2s 4s 8s 16s，總共 31s，第 5 次發出後還要等 32s 才知道第 5 次也超時了，所以，總共需要 1s 2s 4s 8s 16s 32s 63s，TCP 才會斷開這個連線。使用三個 TCP 引數來調整行為：tcp_synack_retries 減少重試次數；tcp_max_syn_backlog，增大 SYN 連線數；tcp_abort_on_overflow 決定超出能力時的行為。 三次握手 的目的是 為了防止已失效的連線 connect 請求報文段傳送到了服務端，因而產生錯誤，也即為了解決 網路中存在延遲的重複分組 問題。例如：client 發出的第一個連線請求報文段並沒有丟失，而是在某個網路結點長時間的滯留了，以致延誤到連線釋放以後的某個時間才到達 server。本來這是一個早已失效的報文段。但 server 收到此失效的連線請求報文段後，就誤認為是 client 發出的一個新的連線請求。於是就向 client 發出確認報文段，同意建立連線。假設不採用 三次握手，那麼只要 server 發出確認，新的連線就建立了。由於現在 client 並沒有發出建立連線的請求，因此不會理睬 server 的確認，也不會向 server 傳送資料。但 server 卻以為新的運輸連線已經建立，並一直等待 client 發來資料。這樣，server 的很多資源就白白浪費掉了。採用 三次握手 的辦法可以防止上述現象發生，client 不會向 server 的確認發出確認。server 由於收不到確認，就知道 client 並沒有要求建立連線。 資源使用 主機收到一個 TCP 包時，用兩端的 IP 地址與埠號來標識這個 TCP 包屬於哪個 session。使用一張表來儲存所有的 session，表中的每條稱作 Transmission Control Block（TCB），tcb 結構的定義包括連線使用的源埠、目的埠、目的 ip、序號、應答序號、對方視窗大小、己方視窗大小、tcp 狀態、tcp 輸入 輸出佇列、應用層輸出佇列、tcp 的重傳有關變數等。 伺服器端的連線數量是無限的，只受記憶體的限制。客戶端的連線數量，過去由於在傳送第一個 SYN 到伺服器之前需要先分配一個隨機空閒的埠，這限制了客戶端 IP 地址的對外發出連線的數量上限。從 Linux 42 開始，有了 socket 選項 IP_BIND_ADDRESS_NO_PORT，它通知 Linux 核心不保留 usingbind 使用埠號為 0 時內部使用的臨時埠（ephemeral port），在 connect 時會自動選擇埠以組成獨一無二的四元組（同一個客戶端埠可用於連線不同的伺服器套接字；同一個伺服器埠可用於接受不同客戶端套接字的連線）。 對於不能確認的包、接收但還沒讀取的資料，都會佔用作業系統的資源。 資料傳輸 在 TCP 的資料傳送狀態，很多重要的機制保證了 TCP 的可靠性和強壯性。它們包括：使用序號，對收到的 TCP 報文段進行排序以及檢測重複的資料；使用校驗和檢測報文段的錯誤，即無錯傳輸；使用確認和計時器來檢測和糾正丟包或延時；流控制（Flow control）；擁塞控制（Congestion control）；丟失包的重傳。 可靠傳輸 通常在每個 TCP 報文段中都有一對序號和確認號。TCP 報文傳送者稱自己的位元組流的編號為序號（sequence number），稱接收到對方的位元組流編號為確認號。TCP 報文的接收者為了確保可靠性，在接收到一定數量的連續位元組流後才傳送確認。這是對 TCP 的一種擴充套件，稱為選擇確認。選擇確認使得 TCP 接收者可以對亂序到達的資料塊進行確認。每一個位元組傳輸過後，SN 號都會遞增 1。 透過使用序號和確認號，TCP 層可以把收到的報文段中的位元組按正確的順序交付給應用層。序號是 32 位的無符號數，在它增大到 2321 時，便會迴繞到 0。對於初始化序列號 ISN 的選擇是 TCP 中關鍵的一個操作，它可以確保強壯性和安全性。 TCP 協議使用序號標識每端發出的位元組的順序，從而另一端接收資料時可以重建順序，無懼傳輸時的或丟包。在傳送第一個包時（SYN 包），選擇一個隨機數作為序號的初值，以剋制 傳送確認包（Acks），攜帶了接收到的對方發來的位元組流的編號，稱為確認號，以告訴對方已經成功接收的資料流的位元組位置。Ack 並不意味著資料已經交付了上層應用程式。 可靠性透過傳送方檢測到丟失的傳輸資料並重傳這些資料。包括超時重傳（Retransmission timeout，RTO）與重複累計確認（duplicate cumulative acknowledgements，DupAcks）。 基於重複累計確認的重傳 如果一個包（不妨設它的序號是 100，即該包始於第 100 位元組）丟失，接收方就不能確認這個包及其以後的包，因為採用了累計 ack。接收方在收到 100 以後的包時，發出對包含第 99 位元組的包的確認。這種重複確認是包丟失的訊號。傳送方如果收到 3 次對同一個包的確認，就重傳最後一個未被確認的包。閾值設為 3 被證實可以減少亂序包導致的無作用的重傳（spurious retransmission）現象。 選擇性確認（SACK）的使用能明確反饋哪個包收到了，極大改善了 TCP 重傳必要的包的能力。 超時重傳 傳送方使用一個保守估計的時間作為收到資料包的確認的超時上限。如果超過這個上限仍未收到確認包，傳送方將重傳這個資料包。每當傳送方收到確認包後，會重置這個重傳定時器。典型地，定時器的值設定為 text smoothed RTT max G 4timestext RTT variation 其中 G 是時鐘粒度。 進一步，如果重傳定時器被觸發，仍然沒有收到確認包，定時器的值將被設為前次值的二倍（直到特定閾值）。這是由於存在一類透過欺騙傳送者使其重傳多次，進而壓垮接收者的攻擊，而使用前述的定時器策略可以避免此類中間人攻擊方式的拒絕服務攻擊。 資料傳輸舉例 傳送方首先發送第一個包含序列號為 1（可變化）和 1460 位元組資料的 TCP 報文段給接收方。接收方以一個沒有資料的 TCP 報文段來回復（只含報頭），用確認號 1461 來表示已完全收到並請求下一個報文段。 傳送方然後傳送第二個包含序列號為 1461，長度為 1460 位元組的資料的 TCP 報文段給接收方。正常情況下，接收方以一個沒有資料的 TCP 報文段來回復，用確認號 2921（14611460）來表示已完全收到並請求下一個報文段。傳送接收這樣繼續下去。 然而當這些資料包都是相連的情況下，接收方沒有必要每一次都回應。比如，他收到第 1 到 5 條 TCP 報文段，只需回應第五條就行了。在例子中第 3 條 TCP 報文段被丟失了，所以儘管他收到了第 4 和 5 條，然而他只能回應第 2 條。 傳送方在傳送了第三條以後，沒能收到回應，因此當時鍾過時時，他重發第三條。（每次傳送者傳送一條 TCP 報文段後，都會再次啟動一次時鐘：RTT）。 這次第三條被成功接收，接收方可以直接確認第 5 條，因為 4，5 兩條已收到。 校驗和 TCP 的 16 位的校驗和（checksum）的計算和檢驗過程如下：傳送者將 TCP 報文段的頭部和資料部分的和計算出來，再對其求反碼（一的補數），就得到了校驗和，然後將結果裝入報文中傳輸。（這裡用反碼和的原因是這種方法的迴圈進位使校驗和可以在 16 位、32 位、64 位等情況下的計算結果再疊加後相同）接收者在收到報文後再按相同的演算法計算一次校驗和。這裡使用的反碼使得接收者不用再將校驗和欄位儲存起來後清零，而可以直接將報文段連同校驗加總。如果計算結果是全部為一，那麼就表示了報文的完整性和正確性。 注意：TCP 校驗和也包括了 96 位的偽頭部，其中有源地址、目的地址、協議以及 TCP 的長度。這可以避免報文被錯誤地路由。 按現在的標準，TCP 的校驗和是一個比較脆弱的校驗。出錯機率高的資料鏈路層需要更高的能力來探測和糾正連線錯誤。TCP 如果是在今天設計的，它很可能有一個 32 位的 CRC 校驗來糾錯，而不是使用校驗和。但是透過在第二層使用通常的 CRC 校驗或更完全一點的校驗可以部分地彌補這種脆弱的校驗。第二層是在 TCP 層和 IP 層之下的，比如 PPP 或乙太網，它們使用了這些校驗。但是這也並不意味著 TCP 的 16 位校驗和是冗餘的，對於因特網傳輸的觀察，表明在受 CRC 校驗保護的各跳之間，軟體和硬體的錯誤通常也會在報文中引入錯誤，而端到端的 TCP 校驗能夠捕捉到大部分簡單的錯誤。 這就是應用中的端到端原則。 流量控制 流量控制用來避免主機分組傳送得過快而使接收方來不及完全收下，一般由接收方通告給傳送方進行調控。 TCP 使用實現流量控制。接收方在 接收視窗 域指出還可接收的位元組數量。傳送方在沒有新的確認包的情況下至多傳送 接收視窗 允許的位元組數量。接收方可修改 接收視窗 的值。 當接收方宣佈接收視窗的值為 0，傳送方停止進一步傳送資料，開始了 保持定時器（persist timer），以避免因隨後的修改接收視窗的資料包丟失使連線的雙側進入死鎖，傳送方無法發出資料直至收到接收方修改視窗的指示。當 保持定時器 到期時，TCP 傳送方嘗試恢復傳送一個小的 ZWP 包（Zero Window Probe），期待接收方回覆一個帶著新的接收視窗大小的確認包。一般 ZWP 包會設定成 3 次，如果 3 次過後還是 0 的話，有的 TCP 實現就會發 RST 把連結斷了。 如果接收方以很小的增量來處理到來的資料，它會發布一系列小的接收視窗。這被稱作愚蠢視窗綜合症，因為它在 TCP 的資料包中傳送很少的一些位元組，相對於 TCP 包頭是很大的開銷。解決這個問題，就要避免對小的 window size 做出響應，直到有足夠大的 window size 再響應： 接收端使用 David D Clark 演算法：如果收到的資料導致 window size 小於某個值，可以直接 ack 把 window 給關閉了，阻止了傳送端再發資料。等到接收端處理了一些資料後 windows size 大於等於了 MSS，或者接收端 buffer 有一半為空，就可以把 window 開啟讓傳送端再發資料過來。 傳送端使用 Nagle 演算法來延時處理，條件一：Window SizeMSS 且 Data Size MSS；條件二：等待時間或是超時 200ms，這兩個條件有一個滿足，才會發資料，否則就是在積累資料。Nagle 演算法預設是開啟的，所以對於一些需要小包場景的程式 比如像 telnet 或 ssh 這樣的互動性程式，需要關閉這個演算法。可以在 Socket 設定 TCP_NODELAY 選項來關閉這個演算法。 擁塞控制 擁塞控制是傳送方根據網路的承載情況控制分組的傳送量，以獲取高效能又能避免擁塞崩潰（congestion collapse，網路效能下降幾個數量級）。這在網路流之間產生近似分配。 傳送方與接收方根據確認包或者包丟失的情況，以及定時器，估計網路擁塞情況，從而修改資料流的行為，這稱為擁塞控制或網路擁塞避免。 TCP 的現代實現包含四種相互影響的擁塞控制演算法：慢開始、擁塞避免、快速重傳、快速恢復。 此外，傳送方採取 超時重傳（retransmission timeout，RTO），這是估計出來回通訊延遲 RTT 以及 RTT 的方差。 RFC793 中定義的計算 SRTT 的經典演算法：指數加權移動平均（Exponential weighted moving average） 先採樣 RTT，記下最近好幾次的 RTT 值。 做平滑計算 SRTT 公式為：SRTTalpha SRTT1alphaRTT，其中 α 取值在 08 到 09 之間 計算 RTO，公式：RTOmin UBOUNDmax LBOUNDbetaSRTT，其中 UBOUND 是最大的 timeout 時間上限值，LBOUND 是最小的 timeout 時間下限值，β 值一般在 13 到 20 之間。 1987 年，出現計算 RTT 的或 TCP 時間戳，最大特點是 忽略重傳，不把重傳的 RTT 做取樣。但是，如果在某一時間，網路閃動，突然變慢了，產生了比較大的延時，這個延時導致要重傳所有的包（因為之前的 RTO 很小），於是，因為重傳的不算，所以，RTO 就不會被更新，這是一個災難。為此，Karn 演算法一發生重傳，就對現有的 RTO 值翻倍。這就是的 Exponential backoff。 1988 年，在中給出範雅各布森演算法取平均以獲得平滑往返時延（Smoothed Round Trip Time，SRTT），作為最終的 RTT 估計值。這個演算法在被用在今天的 TCP 協議中： SRTTSRTTalphaRTTSRTT DevRTT1betaDevRTTbetaleftvert RTTSRTT rightvert RTOmuSRTTpartialDevRTT 其中：DevRTT 是 Deviation RTT。在 Linux 下，α 0125，β 025， μ 1， 4 目前有很多 TCP 擁塞控制演算法在研究中。 最大分段大小 最大分段大小 MSS 是在單個分段中 TCP 願意接受的資料的位元組數最大值。MSS 應當足夠小以避免 IP 分片，它會導致丟包或過多的重傳。在 TCP 連線建立時，雙端在 SYN 報文中用 MSS 選項宣佈各自的 MSS，這是從雙端各自直接相連的資料鏈路層的最大傳輸單元（MTU）的尺寸減去固定的 IP 首部和 TCP 首部長度。乙太網 MTU 為 1500 位元組， MSS 值可達 1460 位元組。使用 IEEE 8023 的 MTU 為 1492 位元組，MSS 可達 1452 位元組。如果目的 IP 地址為 非本地的，MSS 通常的預設值為 536（這個預設值允許 20 位元組的 IP 首部和 20 位元組的 TCP 首部以適合 576 位元組 IP 資料報）。此外，傳送方可用推匯出從傳送方到接收方的網路路徑上的最小 MTU，以此動態調整 MSS 以避免網路 IP 分片。 MSS 釋出也被稱作 MSS 協商（MSS negotiation）。嚴格講，這並非是協商出來一個統一的 MSS 值，TCP 允許連線兩端使用各自不同的 MSS 值。 例如，這會發生在參與 TCP 連線的一臺裝置使用非常少的記憶體處理到來的 TCP 分組。 選擇確認 最初採取累計確認的 TCP 協議在丟包時效率很低。例如，假設透過 10 個分組發出了 1 萬個位元組的資料。如果第一個分組丟失，在純粹的累計確認協議下，接收方不能說它成功收到了 1000 到 9999 位元組，但未收到包含 0 到 999 位元組的第一個分組。因而，傳送方可能必須重傳所有 1 萬個位元組。 為此，TCP 採取了 選擇確認（selective acknowledgment，SACK）選項。RFC 2018 對此定義為允許接收方確認它成功收到的分組的不連續的塊，以及基礎 TCP 確認的成功收到最後連續位元組序號。這種確認可以指出 SACK block，包含了已經成功收到的連續範圍的開始與結束位元組序號。在上述例子中，接收方可以發出 SACK 指出序號 1000 到 9999，傳送方因此知道只需重發第一個分組（位元組 0 到 999）。 TCP 傳送方會把亂序收包當作丟包，因此會重傳亂序收到的包，導致連線的效能下降。重複 SACK 選項（duplicateSACK option）是定義在 RFC 2883 中的 SACK 的一項擴充套件，可解決這一問題。接收方發出 DACK 指出沒有丟包，接收方恢復到高傳輸率。DSACK 使用了 SACK 的第一個段來做標誌， 如果 SACK 的第一個段的範圍被 ACK 所覆蓋，那麼就是 DSACK 如果 SACK 的第一個段的範圍被 SACK 的第二個段覆蓋，那麼就是 DSACK DSACK 旨在告訴傳送端：收到了重複的資料，資料包沒有丟，丟的是 ACK 包；或者 Fast Retransmit 演算法 觸發的重傳不是因為發出去的包丟了，也不是因為回應的 ACK 包丟了，而是因為網路延時導致的 reordering。 SACK 選項並不是強制的。僅當雙端都支援時才會被使用。TCP 連線建立時會在 TCP 頭中協商 SACK 細節。在 Linux 下，可以透過 tcp_sack 引數開啟 SACK 功能（Linux 24 後預設開啟）。Linux 下的 tcp_dsack 引數用於開啟 DSACK 功能（Linux 24 後預設開啟）。選擇確認也用於流控制傳輸協議 SCTP TCP 視窗縮放選項 TCP 視窗尺寸域控制資料包在 2 至 65535 位元組。RFC 1323 定義的用於把最大視窗尺寸從 65535 位元組擴大至 1G 位元組。擴大視窗尺寸是的需要。 視窗縮放選項盡在 TCP 三次握手時雙端在 SYN 包中獨立指出這個方向的縮放係數。該值是 16 位元視窗尺寸的向左位移數，從 0 表示不位移 至 14。 某些路由器或分組防火牆會重寫視窗縮放選項，這可能導致不穩定的網路傳輸。 TCP 時間戳 RFC 1323 定義了 TCP 時間戳，並不對應於系統時鐘，使用隨機值初始化。許多作業系統每毫秒增加一次時間戳；但 RFC 只規定 tick 應當成比例。 有兩個時間戳域 4 位元組的傳送時間戳值 4 位元組的響應回覆時間戳值（最近收到資料的時間戳） TCP 時間戳用於 防止序列號迴繞演算法（Protection Against Wrapped Sequence numbers，PAWS），細節見 RFC 1323。PAWS 用於接收視窗跨序號迴繞邊界。這種情形下一個包可能會重傳以回答問題：是否是第一個還是第二個 4 GB 的序號？ 時間戳可以打破這一問題。 另外，Eifel 檢測演算法（ RFC 3522 ）使用 TCP 時間戳確定如果重傳發生是因為丟包還是簡單亂序。 最近統計表明時間戳的採用率停滯在～40，這歸因於 Windows 伺服器從 Windows Server 2008 起降低了支援。 帶外資料 （OOB）是指對緊急資料，中斷或放棄排隊中的資料流；接收方應立即處理緊急資料。完成後，TCP 通知應用程式恢復流佇列的正常處理。 OOB 並不影響網路，緊急 僅影響遠端端的處理。這一協議很少被實現。 強制資料遞交 正常情況下，TCP 等待 200 ms 以準備一個完整分組發出（納格演算法試圖把小的資訊組裝為單一的包）。這產生了小的、但潛在很嚴重的延遲並在傳遞一個檔案時不斷重複延遲。例如，典型傳送塊是 4 KB，典型的 MSS 是 1460 位元組，在 10 Mbits 乙太網上發出兩個包，每個耗時約～12 ms，隨後是剩餘 1176 個位元組的包，之後是 197 ms 停頓因為 TCP 等待裝滿緩衝區。 對於 telnet，每次使用者擊鍵的回應，如果有 200 ms 將會非常煩人。 socket 選項 TCP_NODELAY 能放棄預設的 200 ms 傳送延遲。應用程式使用這個 socket 選項強制發出資料。 RFC 定義了 PSH 能立即發出位元。Berkeley 套接字不能控制或指出這種情形，只能由協議棧控制。 終結通路 連線終止使用了四路握手過程（或稱四次握手，），在這個過程中連線的每一側都獨立地被終止。當一個端點要停止它這一側的連線，就向對側傳送 FIN，對側回覆 ACK 表示確認。因此，拆掉一側的連線過程需要一對 FIN 和 ACK，分別由兩側端點發出。 首先發出 FIN 的一側，如果給對側的 FIN 響應了 ACK，那麼就會超時等待 2MSL 時間，然後關閉連線。在這段超時等待時間內，本地的埠不能被新連線使用；避免延時的包的到達與隨後的新連線相混淆。RFC793 定義了 MSL 為 2 分鐘，Linux 設定成了 30s。引數 tcp_max_tw_buckets 控制併發的 TIME_WAIT 的數量，預設值是 180000，如果超限，那麼，系統會把多的 TIME_WAIT 狀態的連線給 destory 掉，然後在日誌裡打一個警告（如：time wait bucket table overflow） 連線可以工作在狀態。即一側關閉了連線，不再發送資料；但另一側沒有關閉連線，仍可以傳送資料。已關閉的一側仍然應接收資料，直至對側也關閉了連線。 也可以透過測三 zhhans 次；zhhant 路 握手關閉連線。主機 A 發出 FIN，主機 B 回覆 FIN ACK，然後主機 A 回覆 ACK 一些主機（如 Linux 或 HPUX）的 TCP 棧能實現半雙工關閉序列。這種主機如果主動關閉一個連線但還沒有讀完從這個連線已經收到的資料，該主機發送 RST 代替 FINSection 42213 in RFC 1122。這使得一個 TCP 應用程式能確認遠端應用程式已經讀了所有已傳送資料，並等待遠端側發出的 FIN。但是遠端的 TCP 棧不能區分 Connection Aborting RST 與 Data Loss RST，兩種原因都會導致遠端的 TCP 棧失去所有的收到資料。 一些應用協議使用 TCP openclose handshaking，因為應用協議的 TCP openclose handshaking 可以發現主動關閉的 RST 問題。例如： s connect remote send s data close s TCPIP 棧採用上述方法不能保證所有資料到達對側，如果未讀資料已經到達對側。 狀態編碼 下表為 TCP 狀態碼列表，以 S 指代伺服器，C 指代客戶端，SC 表示兩者，SC 表示兩者之一： LISTEN S 伺服器等待從任意遠端 TCP 埠的連線請求。偵聽狀態。 SYNSENT C 客戶在傳送連線請求後等待匹配的連線請求。透過 connect 函式向伺服器發出一個同步（SYNC）訊號後進入此狀態。 SYNRECEIVED S 伺服器已經收到併發送同步（SYNC）訊號之後等待確認（ACK）請求。 ESTABLISHED SC 伺服器與客戶的連線已經開啟，收到的資料可以傳送給使用者。資料傳輸步驟的正常情況。此時連線兩端是平等的。這稱作全連線。 FINWAIT1 SC （伺服器或客戶）主動關閉端呼叫 close函式發出 FIN 請求包，表示本方的資料傳送全部結束，等待 TCP 連線另一端的 ACK 確認包或 FINACK 請求包。 FINWAIT2 SC 主動關閉端在 FINWAIT1 狀態下收到 ACK 確認包，進入等待遠端 TCP 的連線終止請求的半關閉狀態。這時可以接收資料，但不再發送資料。 CLOSEWAIT SC 被動關閉端接到 FIN 後，就發出 ACK 以回應 FIN 請求，並進入等待本地使用者的連線終止請求的半關閉狀態。這時可以傳送資料，但不再接收資料。 CLOSING SC 在發出 FIN 後，又收到對方發來的 FIN 後，進入等待對方對己方的連線終止（FIN）的確認（ACK）的狀態。少見。 LASTACK SC 被動關閉端全部資料傳送完成之後，向主動關閉端傳送 FIN，進入等待確認包的狀態。 TIMEWAIT SC 主動關閉端接收到 FIN 後，就傳送 ACK 包，等待足夠時間以確保被動關閉端收到了終止請求的確認包。（按照 RFC 793，一個連線可以在 TIMEWAIT 保證最大四分鐘，即最大分段壽命（maximum segment lifetime）的 2 倍） CLOSED SC 完全沒有連線。 埠 TCP 使用了通訊埠的概念來標識傳送方和接收方的應用層。對每個 TCP 連線的一端都有一個相關的 16 位元的無符號埠號分配給它們。埠被分為三類：眾所周知的、註冊的和動態 私有的。眾所周知的埠號是由因特網賦號管理局（IANA）來分配的，並且通常被用於系統一級或根程序。眾所周知的應用程式作為伺服器程式來執行，並被動地偵聽經常使用這些埠的連線。例如：FTP、TELNET、SMTP、HTTP、IMAP、POP3 等。註冊的埠號通常被用來作為終端使用者連線伺服器時短暫地使用的源埠號，但它們也可以用來標識已被第三方註冊了的、被命名的服務。動態 私有的埠號在任何特定的 TCP 連線外不具有任何意義。可能的、被正式承認的埠號有 65535 個。 封包結構 TCP 表頭 偏移 位元組 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 位元組 位元 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0 0 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 來源連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 目的連線埠 4 32 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 序列號碼 8 64 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 確認號碼（當 ACK 設定） 12 96 資料偏移 資料偏移 資料偏移 資料偏移 保留 0 0 0 保留 0 0 0 保留 0 0 0 NS CWR ECE URG ACK PSH RST SYN FIN 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 視窗大小 16 128 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 校驗和 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 緊急指標（當 URG 設定） 20 160 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 選項（如果資料偏移 5，需要在結尾新增 0。） 來源連線埠（16 位元長）－辨識傳送連線埠 目的連線埠（16 位元長）－辨識接收連線埠 序列號（seq，32 位元長） 如果含有同步化旗標（SYN），則此為最初的序列號；第一個資料位元的序列碼為本序列號加一。 如果沒有同步化旗標（SYN），則此為第一個資料位元的序列碼。 確認號（ack，32 位元長） 期望收到的資料的開始序列號。也即已經收到的資料的位元組長度加 1。 資料偏移（4 位元長） 以 4 位元組為單位計算出的資料段開始地址的偏移值。 保留（3 位元長） 須置 0 標誌符（9 位元長） NSECNnonce。ECN 顯式擁塞通知（Explicit Congestion Notification）是對 TCP 的擴充套件，定義於 RFC 3540 （2003）。ECN 允許擁塞控制的端對端通知而避免丟包。ECN 為一項可選功能，如果底層網路設施支援，則可能被啟用 ECN 的兩個端點使用。在 ECN 成功協商的情況下，ECN 感知路由器可以在 IP 頭中設定一個標記來代替丟棄資料包，以標明阻塞即將發生。資料包的接收端回應傳送端的表示，降低其傳輸速率，就如同在往常中檢測到包丟失那樣。 CWRCongestion Window Reduced，定義於 RFC 3168（2001）。 ECEECNEcho 有兩種意思，取決於 SYN 標誌的值，定義於 RFC 3168（2001）。 URG 為 1 表示高優先順序資料包，緊急指標欄位有效。 ACK 為 1 表示確認號欄位有效 PSH 為 1 表示是帶有 PUSH 標誌的資料，指示接收方應該儘快將這個報文段交給應用層而不用等待緩衝區裝滿。 RST 為 1 表示出現嚴重差錯。可能需要重新建立 TCP 連線。還可以用於拒絕非法的報文段和拒絕連線請求。 SYN 為 1 表示這是連線請求或是連線接受請求，用於建立連線和使順序號同步 FIN 為 1 表示傳送方沒有資料要傳輸了，要求釋放連線。 視窗（WIN，16 位元長） 表示從確認號開始，本報文的傳送方可以接收的位元組數，即接收視窗大小。用於流量控制。 校驗和（Checksum，16 位元長） 對整個的 TCP 報文段，包括 TCP 頭部和 TCP 資料，以 16 位字進行計算所得。這是一個強制性的欄位。 緊急指標（16 位元長） 本報文段中的緊急資料的最後一個位元組的序號。 選項欄位 最多 40 位元組。每個選項的開始是 1 位元組的 kind 欄位，說明選項的型別。 0：選項表結束（1 位元組） 1：無操作（1 位元組）用於選項欄位之間的字邊界對齊。 2：最大報文段長度（4 位元組，Maximum Segment Size，MSS）通常在建立連線而設定 SYN 標誌的資料包中指明這個選項，指明本端所能接收的最大長度的報文段。通常將 MSS 設定為（MTU40）位元組，攜帶 TCP 報文段的 IP 資料報的長度就不會超過 MTU（MTU 最大長度為 1518 位元組，最短為 64 位元組），從而避免本機發生 IP 分片。只能出現在同步報文段中，否則將被忽略。 3：視窗擴大因子（3 位元組，wscale），取值 014。用來把 TCP 的視窗的值左移的位數，使視窗值乘倍。只能出現在同步報文段中，否則將被忽略。這是因為現在的 TCP 接收資料緩衝區（接收視窗）的長度通常大於 65535 位元組。 4：sackOK 傳送端支援並同意使用 SACK 選項。 5：SACK 實際工作的選項。 8：時間戳（10 位元組，TCP Timestamps Option，TSopt） 傳送端的時間戳（Timestamp Value field，TSval，4 位元組） 時間戳回顯應答（Timestamp Echo Reply field，TSecr，4 位元組） 19：MD5 摘要，將 TCP 偽首部、校驗和為 0 的 TCP 首部、TCP 資料段、通訊雙方約定的金鑰（可選）計算出 MD5 摘要值並附加到該選項中，作為類似對 TCP 報文的簽名。透過 RFC 2385 引入，主要用於增強 BGP 通訊的安全性。 29：安全摘要，透過 RFC 5925 引入，將 MD5 摘要 的雜湊方法更換為 SHA 雜湊演算法。 發展過程 TCP 是一個複雜的但同時又是在發展之中的協議。儘管許多重要的改進被提出和實施，發表於 1981 年的 RFC793 中說明的 TCP（TCPTahoe）的許多基本操作還是未作多大改動。RFC1122：《因特網對主機的要求》闡明瞭許多 TCP 協議的實現要求。RFC2581：《TCP 的擁塞控制》是一篇近年來關於 TCP 的很重要的 RFC，描述了更新後的避免過度擁塞的演算法。寫於 2001 年的 RFC3168 描述了對明顯擁塞的報告，這是一種擁塞避免的訊號量機制。在 21 世紀早期，在所有因特網的資料包中，通常有大約 95 的包使用了 TCP 協議。常見的使用 TCP 的應用層有 HTTPHTTPS（全球資訊網協議），SMTPPOP3IMAP（電子郵件協議）以及 FTP（檔案傳輸協議）。這些協議在今天被廣泛地使用，這證明了它們的原作者的創造是卓越的。 最近，一個新協議已經被加州理工學院的科研人員開發出來，命名為 FAST TCP（基於快速活動佇列管理的規模可變的傳輸控制協議）。它使用排隊延遲作為擁塞控制訊號；但是因為端到端的延遲通常不僅僅包括排隊延遲，所以 FAST TCP（或更一般地，所有基於排隊延遲的演算法）在實際網際網路中的能否工作仍然是一個沒有解決的問題。 應用 TCP 並不是對所有的應用都適合，一些新的帶有一些內在的脆弱性的運輸層協議也被設計出來。比如，即時應用並不需要甚至無法忍受 TCP 的可靠傳輸機制。在這種型別的應用中，通常允許一些丟包、出錯或擁塞，而不是去校正它們。例如通常不使用 TCP 的應用有：流媒體、網路遊戲、IP 電話（VoIP）等等。任何不是很需要可靠性或者是想將功能減到最少的應用可以避免使用 TCP。在很多情況下，當只需要多路複用應用服務時，使用者資料報協議（UDP）可以代替 TCP 為應用提供服務。 除外，由於 TCP 的實現是由作業系統提供，而 TCP 的悠久歷史、系統級別的配置機制，一些特性在特定的網路環境下會成為一種累贅而且無法最佳化，所以也有一些透過在 UDP 上重新實現使用者層級的類似 TCP 的面向連線的、可靠的、基於位元組流的類傳輸層協議，來代替 TCP，例如基於 UDP 的資料傳輸協議、QUIC。 參見 TCP 校驗和解除安裝 TCPUDP 埠列表 SCTP 連線重置 擁塞控制 參考資料 Timothy S Ramteke Networks Second Edition PrenticeHall 2001 ISBN 0139012656 Torsten Braun Martina Zitterbart Hochleistungskommunikation Bd2 Transportdienste und Transportprotokolle Oldenbourg 1996 ISBN 9783486230888 外部連結 （1981 年） （1992 年） （2001 年） （2003 年） Category 傳輸層協議 CategoryTCPIP