TCP協議規範

 

傳輸控制協議(Transmission Control Protocol, TCP)

TCP 協議主爲了在主機間實現高可靠性的包交換傳輸協議。本文將描述協議標準和實現的一些方法。因爲計算機網絡在現代社會中已經是不可缺少的了,TCP協議主要 在網絡不可靠的時候完成通信,對軍方可能特別有用,但是對於政府和商用部門也適用。TCP是面向連接的端到端的可靠協議。它支持多種網絡應用程序。TCP 對下層服務沒有多少要求,它假定下層只能提供不可靠的數據報服務,它可以在多種硬件構成的網絡上運行。下面的圖是TCP在層次式結構中的位置,它的下層是 IP協議,TCP可以根據IP協議提供的服務傳送大小不定的數據,IP協議負責對數據進行分段,重組,在多種網絡中傳送。

網絡層次

TCP 的上面就是應用程序,下面是IP協議,上層接口包括一系列類似於操作系統中斷的調用。對於上層應用程序來說,TCP應該能夠異步傳送數據。下層接口我們假 定爲IP協議接口。爲了在並不可靠的網絡上實現面向連接的可靠的傳送數據,TCP必須解決可靠性,流量控制的問題,必須能夠爲上層應用程序提供多個接口, 同時爲多個應用程序提供數據,同時TCP必須解決連接問題,這樣TCP才能稱得上是面向連接的,最後,TCP也必須能夠解決通信安全性的問題。

網 絡環境包括由網關(或其它設備)連接的網絡,網絡可以是局域網也可以是一些城域網或廣域網,但無論它們是什麼,它們必須是基於包交換的。主機上不同的協議 有不同的端口號,一對進程通過這個端口號進行通信。這個通信不包括計算機內的I/O操作,只包括在網絡上進行的操作。網絡上的計算機被看作包傳送的源和目 的結點。特別應該注意的是:計算機中的不同進程可能同時進行通信,這時它們會用端口號進行區別,不會把發向A進程的數據由B進程接收的。

進 程爲了傳送數據會調用TCP,將數據和相應的參數傳送給TCP,於是TCP會將數據傳送到目的TCP那裏,當然這是通過將TCP包打包在IP包內在網絡上 傳送達到的。接收方TCP在接收到數據後會通信上層應用程序,TCP會保證接收數據順序的正確性。雖然下層協議可能不會保證順序是正確的。這裏需要說明的 是網關在接收到這個包後,會將包解開,看看是不是已經到目的地了,如果沒有到,應該走什麼路由達到目的地,在決定後,網關會根據下一個網絡內的協議情況再 次將TCP包打包傳送,如果需要,還要把這個包再次分成幾段再傳送。這個落地檢查的過程是一個耗時的過程。從上面,我們可以看出TCP傳送的基本過程,當 然具體過程可能要複雜得多。

在 實現TCP的主機上,TCP可以被看成是一個模塊,和文件系統區別不大,TCP也可以調用一些操作系統的功能,TCP不直接和網絡打交道,控制網絡的任務 由專門的設備驅動模塊完成。TCP只是調用IP接口,IP向TCP提供所有TCP需要的服務。通過下圖我們可以更清楚地看到TCP協議的結構。

網絡層次聯繫

上 面已經說過了,TCP連接是可靠的,而且保證了傳送數據包的順序,保證順序是用一個序號來保證的。響應包內也包括一個序列號,表示接收方準備好這個序號的 包。在TCP傳送一個數據包時,它同時把這個數據包放入重發隊列中,同時啓動記數器,如果收到了關於這個包的確認信息,將此包從隊列中刪除,如果計時超時 則需要重新發送此包。請注意,從TCP返回的確認信息並不保證最終接收者接收到數據,這個責任由接收方負責。

每 個用於傳送TCP的通道都有一個端口標記,因爲這個標記是由每個TCP終端確定的,因此TCP可能不唯一,爲了保證這個數值的唯一,要使用網絡地址和端口 號的組合達到唯一標識的目的,我們稱這個爲了套接字(Socket),一個連接由連接兩端的套接字標識,本地的套接字可能和不同的外部套接字通信,這種通 信是全雙工的。

通 過向本地端口發送OPEN命令及外部套接字參數建立連接,TCP返回一個標記這個連接的名稱,以後如果用戶需要使用這個名稱標記這個連接。爲了保存這個連 接的信息,我們假設有一個稱爲傳輸控制塊(Transmission Control Block,TCB)的東西來保存。OPEN命令還指定這個連接的建立是主動請求還是被動等待請求。下面我們要涉及具體的功能了,TCP段以 internet數據報的形式傳送。IP包頭傳送不同的信息域,包括源地址和目的地址。TCP頭跟在internet包頭後面,提供了一些專用於TCP協 議的信息。下圖是TCP包頭格式圖:

TCP包頭格式

源端口:16位;

目的端口:16位

序列碼:32位,當SYN出現,序列碼實際上是初始序列碼(ISN),而第一個數據字節是ISN+1;

確認碼:32位,如果設置了ACK控制位,這個值表示一個準備接收的包的序列碼;

數據偏移量:4位,指示何處數據開始;

保留:6位,這些位必須是0;

控制位:6位;

窗口:16位;

校驗位:16位;

優先指針:16位,指向後面是優先數據的字節;

選項:長度不定;但長度必須以字節記;選項的具體內容我們結合具體命令來看;

填充:不定長,填充的內容必須爲0,它是爲了保證包頭的結合和數據的開始處偏移量能夠被32整除;

 

我們前面已經說過有一個TCB的東西了,TCB裏有存儲了包括髮送方,接收方的套接字,用戶的發送和接收的緩衝區指針等變量。除了這些還有一些變量和發送接收序列號有關:

發送序列變量

SND.UNA - 發送未確認

SND.NXT - 發送下一個

SND.WND - 發送窗口

SND.UP - 發送優先指針

SND.WL1 - 用於最後窗口更新的段序列號

SND.WL2 - 用於最後窗口更新的段確認號

ISS - 初始發送序列號

 

接收序列號

RCV.NXT - 接收下一個

RCV.WND - 接收下一個

RCV.UP - 接收優先指針

IRS - 初始接收序列號

下圖會幫助您瞭解發送序列變量間的關係:

發送序列空間 接收序列空間

當前段變量

SEG.SEQ - 段序列號

SEG.ACK - 段確認標記

SEG.LEN - 段長

SEG.WND - 段窗口

SEG.UP - 段緊急指針

SEG.PRC - 段優先級

連接進程是通過一系列狀態表示的,這些狀態有:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT和 CLOSED。CLOSED表示沒有連接,各個狀態的意義如下:

LISTEN - 偵聽來自遠方TCP端口的連接請求;

SYN-SENT - 在發送連接請求後等待匹配的連接請求;

SYN-RECEIVED - 在收到和發送一個連接請求後等待對連接請求的確認;

ESTABLISHED - 代表一個打開的連接,數據可以傳送給用戶;

FIN-WAIT-1 - 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認;

FIN-WAIT-2 - 從遠程TCP等待連接中斷請求;

CLOSE-WAIT - 等待從本地用戶發來的連接中斷請求;

CLOSING - 等待遠程TCP對連接中斷的確認;

LAST-ACK - 等待原來發向遠程TCP的連接中斷請求的確認;

TIME-WAIT - 等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認;

CLOSED - 沒有任何連接狀態;

TCP連接過程是狀態的轉換,促使發生狀態轉換的是用戶調用:OPEN,SEND,RECEIVE,CLOSE,ABORT和STATUS;傳送過來的數據段,特別那些包括以下標記的數據段SYN,ACK,RST和FIN;還有超時,上面所說的都會時TCP狀態發生變化。

 

下面的圖表示了TCP狀態的轉換,但這圖中沒有包括錯誤的情況和錯誤處理,不要把這幅圖看成是總說明了。

TCP連接狀態圖

 

3.3. 序列號

請 注意,我們在TCP連接中發送的字節都有一個序列號。因爲編了號,所以可以確認它們的收到。對序列號的確認是累積性的,也就是說,如果用戶收到對X的確認 信息,這表示在X以前的數據(不包括X)都收到了。在每個段中字節是這樣安排的:第一個字節在包頭後面,按這個順序排列。我們需要認記實際的序列空間是有 限的,雖然很大,但是還是有限的,它的範圍是0到2的32次方減1。我想熟悉編程的一定知道爲什麼要在計算兩個段是不是相繼的時候要使用2的32次方爲模 了。TCP必須進行的序列號比較操作種類包括以下幾種:

(a) 決定一些發送了的但未確認的序列號;

(b) 決定所有的序列號都已經收到了;

(c) 決定下一個段中應該包括的序列號。

對於發送的數據TCP要接收確認,處理確認時必須進行下面的比較操作:

SND.UNA = 最老的確認了的序列號;

SND.NXT = 下一個要發送的序列號;

SEG.ACK = 接收TCP的確認,接收TCP期待的下一個序列號;

SEG.SEQ = 一個數據段的第一個序列號;

SEG.LEN = 數據段中包括的字節數;

SEG.SEQ+SEG.LEN-1 = 數據段的最後一個序列號。

請注意下面的關係:

SND.UNA < SEG.ACK =< SND.NXT

如果一個數據段的序列號小於等於確認號的值,那麼整個數據段就被確認了。而在接收數據時下面的比較操作是必須的:

RCV.NXT = 期待的序列號和接收窗口的最低沿;

RCV.NXT+RCV.WND-1 = 最後一個序列號和接收窗口的最高沿;

SEG.SEQ = 接收到的第一個序列號;

SEG.SEQ+SEG.LEN-1 = 接收到的最後一個序列號;

 

上面幾個量有如下關係:

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND 或 RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

測試的第一部分是檢查數據段的開始部分是否在接收窗口中,第二部分是檢查數據段的結束部分是否也在接收窗口內;上面兩個檢查通過任何一個就說明它包括窗口要求的數據。實際中的情況會更復雜一些,因爲有零窗口和零數據段長,因此我們有下面四種情況:

段長度

接收窗口

測試

0

0

SEG.SEQ = RCV.NXT

0

>0

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND

>0

0

不可接受

>0

>0

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND或RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

請注意接收窗口的大小可以爲零,在窗口爲零時它只用來接收ACK信息,因此對於一個TCP來說,它可以使用零大小窗口在發送數據的同時接收數據。即使接收窗口的大小爲零,TCP必須處理所有接收到信息的RST和URG域。

我 們也應用計數的方式保護了一些特定的控制信息,這是通過隱式地使用一些控制標記使數據段能夠可靠地重新發送(或確認)爲達到的。控制信息並不在段數據空間 中傳送,因此,我們必須採用隱式指定序列號進行控制。SYN和FIN是需要保護的控制量,這兩個控制量也只在連接打開和關閉時使用。SYN被認爲是在第一 個實際數據之間的數據,而FIN是最後一個實際數據之後的數據。段長度(SEG.LEN)包括數據和序列號空間,如果出現了SYN,那麼SEG.SEQ是 SYN的序列號。

初始序列號選擇

協 議對於特定連接被重複使用沒有什麼限制。連接是由一對套接字定義的。新的連接實例被定義爲連接的另一次恢復,這就帶來了問題:TCP如果確定多個數據段是 從以前連接的另一次恢復中取得的呢?這個問題在連接迅速打開和關閉,或因爲內存原因被關閉然後又迅速建立後顯示特別突出。

爲 了避免混亂,用戶必須避免因此恢復使用某一連接,而使序列號發生混亂。我們必須保證序列號的正確性,即使TCP失敗,根本不知道以前的序列號是什麼的情況 下也要保證序列號的正確性。當新的連接被創建時,產生一個新的初始序列號(ISN)產生子,它用來選擇一個新的32位ISN。產生子和32位時鐘的低度位 字節相關,低位字節的刷新頻率大概是4微秒,因此ISN的循環時間大概是4.55小時。因此我們把網絡包的最長生存時間(MSL)小於4.55小時,因此 我們可以認爲ISN是唯一的。對於每個連接都有發送序列號和接收序列號,初始發送序列號(ISS)由發送TCP選擇,而初始接收序列號是在連接建立過程中 產生的。

對於將要連接或初始化的連接,兩個TCP必須和對方的初始序列號同步。這通過交換一個控制位SYN和初始序列號完成。我們把帶有SYN的數據段稱爲"SYNs"。同步的獲得過程這裏就不重複了,每方必須發送自己的序列號並返回對對方序列號的確認。

1) A --> B SYN 本方序列號是X

2) A <-- B ACK 本方序列號被確認

3) A <-- B SYN 對方序列號是Y

4) A --> B ACK 確認對方序列號

上 面的第2步和第3步可以合併,這時可以成爲3階段,所以我們可以稱它爲三消息握手。這個過程是必須的,因爲序列號不和全局時鐘關聯,TCP也可以有不同的 機制選擇ISN。接收到第一個SYN的接收方不可能知道這個數據段是不是被延時,除非它記住了在連接上使用的最近的序列號(這通常是不可能的),因此它必 須要求發送者確認。

爲 了保證TCP獲得的確認是剛纔發送的段產生的,而不是仍然在網絡中的老數據段產生的,因此TCP必須在MSL時間之內保持沉默。在本文中,我們假設 MSL=2小時,這是出於工程的需要,如果用戶覺得可以,他可以改變MSL。請注意如果TCP重新初始化,而內存中的序列號正在使用,不需要等待,但必須 確認使用的序列號比當前使用的要大。

如果一臺主機在未保留任何序列號的情況下失敗,那麼它應該在MSL時間之內不發出任何數據段。下面將會這一情況進行說明。TCP的實現可以不遵守這個規定,但是這會造成老數據被當成新數據接收,而新數據被當成老數據拒絕的情況。

每 當數據段形成並進入輸出隊列,TCP會爲它指定序列空間中的一個值。TCP中多複本檢測和序列算法都依賴於這個地址空間,在對方發送或接收之前不會超過2 的32次方個包存在於輸出隊列中。所有多餘的數據段都會被刪除。如果沒有這個規定,會出現多個數據段被指定同一個序列號的情況,會造成混亂。數據段中序列 號的多少和數據段中的字節數一樣多。

在 通常情況下,TCP保留下一個要發送的序列號和還未確認的最老的序列號,不要在沒有確認的時候就再次使用,這樣會有些風險,也正是因爲這樣的目的,所以序 列空間很大。對於2M的網絡,要4.5小時來耗盡序列空間,因爲一個數據段可能的最大生存時間也不過十幾分之一秒,這就留下了足夠的空間;而在100M的 網絡上需要5.4分鐘,雖然少了點,但也可以了。

如 果在實現TCP時沒有爲保存序列號留下空間,那清除多餘的包可能就不能實現了,因此推薦這種類型的TCP實現最好在失敗後等待MSL時間,這樣保證多餘的 包被刪除。這種情況有時候也可能會出現在保留序列號的TCP實現中。如果TCP在選擇一個另一個TCP連接正在使用的序列號時,這臺主機突然失敗了,這就 產生了問題。這個問題的實質在於主機不知道它失敗了多久,也不知道多餘的複本是不是還在網絡中。

處理這種問題的方法是等待MSL時間,如果不這樣就要冒着對方錯誤接收數據的危險,要等待的時間也就稱爲“沉默時間”。實現者可以讓用戶選擇是不是等待,但是無論用戶如何也不見得非要等待MSL時間。

3.4. 建立一個連接

建 立連接應用的是三消息握手。如果雙方同時都發送SYN也沒有關係,雙方會發現這個SYN中沒有確認,於是就知道了這種情況,通常來說,應該發送一 個"reset"段來解決這種情況。三消息握手減少了連接失敗的可能性。下面就是一個例子,在尖括號是的就是數據段中的內容和標記。其它的就不多說了。

基本三消息同步

在第2行,TCP A發送SYN初始化序列號,表示它要使用序列號100;第3行中,TCP B給出確認,並且期待着A的帶有序列號101的數據段;第4行,TCP A給出確認,而在第5行,它也給出確認,併發送了一些數據,注意第4行的序列號與第5號的一樣,因爲ACK信息不佔用序列號空間內的序列號。同時產生請求的情況如下圖所示,只複雜一點。

同時連接同步

使 用三消息握手的主要原因是爲了防止使用過期的數據段。爲了這個目的,必須引入新的控制消息,RESET。如果接收TCP處理非同步狀態,在接收到 RESET後返回到LISTEN狀態。如果TCP處理下面幾種狀態ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE- WAIT,CLOSING,LAST-ACK,TIME-WAIT時,放棄連接並通過用戶。我們下面就詳細說明後一種情況。

過期SYN的恢復

通過上面的例子,我們可以看出TCP連接是如何從過期數據段的干擾下恢復的。請注意第4行和第5行中的RST(RESET信號)。

半開連接和其它非正常狀態

如 果一方在未通過另一方的情況下關閉連接,或雙方雖然失敗而不同步的情況我們稱爲半開連接狀態。在一方試圖發送數據時連接會自動RESET。然而這種情況畢 竟屬於不正常情況。應該做出相應的處理。如果A處的連接已經關閉,B處並不知道。當B希望發送數據到A時,就會收到RESET信號,表示這個TCP連接有 誤,要中止當前連接。

假 設A和B兩個進程相互通信的時候A的TCP發生了失敗,A依靠操作系統支持TCP的存在,通常這種情況下會有恢復機制起作用,當TCP重新恢復的時候,A 可能希望從恢復點開始工作。這樣A可能會試圖OPEN連接,然後在這個它認爲還是打開的連接上傳送數據,這時A會從本地(也就是A的)TCP上獲得錯誤消 息“未打開連接”。A的TCP將發送包括SYN的數據段。下面的例子將顯示這一過程:

關開連接檢測

上面這個例子中,A方收到的信息並沒有確認任何東西,這時候A發現出了問題,於是發送了RST控制信息。另一種情況是發生在A失敗,而B方仍然試圖發送數據時,下面的例子可以表示這種情況,請注意第2行中A對B發送來的信息不知所云。

主動方引起半開連接檢測

在下面的例子中,A方和B方進行的被動連接,它們都在等待SYN信息。過期的包傳送到B方使B迴應了,而收到迴應的A卻發現不對頭,傳送RST控制信息,B方返回被動LISTEN狀態。

過期SYN在兩個被動套接字上開始RESET

現實中的情況太多了,我們列舉一些產生RST控制信息的規則如下:通常情況下,RST在收到的信息不是期待的信息時產生。如果在不能確定時不要輕易發送RST控制信息。下面有三類情況:

如果連接已經不存在,而發送來的消息又 不是RST,那麼要返回RST。如果想拒絕對不存在的連接進行SYN,可以使用這種辦法。如果到達的信息有一個ACK域,返回的RST信息可以從ACK域 中取得序列號,如果沒有這個域,就把RST的序列號設置爲0,ACK域被設備爲序列號和到達段長度之和。連接仍然處於CLOSE狀態。

如果連接處於非同步狀態(LISTEN,SYN-SENT,SYN-RECEIVED),而 且收到的確認是對未發出包的確認或是接收到數據段的安全級別與不能連接要求的相一一致時,就發送RST。如果SYN未被確認時,而且收到的數據段的優先級 比要求的優先級要高,那麼要麼提高本地優先級(得事先徵得用戶和系統的許可)要麼發送RST;如果接收數據段的優先級比要求的優先級低,就算是匹配了,當 然如果對方發現優先級不對提高了優先級,在下一個包中提高了優先級,這就不算是匹配了。如果連接已經進入SYN,那麼接收到數據段的優先級必須和本地優先 級一樣,否則發送RST。如果到達的信息有一個ACK域,返回的RST信息可以從ACK域中取得序列號,如果沒有這個域,就把RST的序列號設置爲 0,ACK域被設備爲序列號和到達段長度之和。連接仍然處於與原來相同的狀態。

如果連接處於同步狀態(ESTABLISHED,FIN-WAIT-1,FIN-WAIT- 2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT),任何超出接收窗口的序列號的數據段都產生如下結果:發出一個空確認 數據段,此段中包括當前發送序列號,另外還包括一個確認指出希望接收的下一個數據段的序列號,連接仍然保存在原來的狀態。如果因爲安全級,優先級之類的問 題,那就發送RST信號然後進入CLOSED狀態。

RST過程

除了SYN-SENT狀態外的其它狀態中,所有的RST數據段可以通過檢查SEQ域查明。如果序列號在接收窗口中,RST是有效的。當連接處於SYN-SENT狀態時,如果ACK域確認SYN,那麼RST也是合法的。

RST 的接收方首先確認它的合法性,然後進行狀態轉換。如果接收方處於LISTEN狀態,它就忽略RST包。如果接收方處於SYN-RECEIVED狀態,而且 以前處於LISTEN狀態,接收方返回LISTEN狀態,否則接收方關閉連接進入CLOSED狀態。當接收方處於其它狀態時,直接關閉連接回到 CLOSED狀態。

 

3.5. 關閉連接

CLOSE 是一個操作,它的意思就是“本方已經有數據發送”。由於是全雙工的,所以會造成一些麻煩,因爲接收方對於處理接收方的連接有點麻煩。我們以一種簡單的方式 對待CLOSE,發送CLOSE的一方在接收到對方的CLOSED之前,還要繼續接收數據。因此程序可以在一個CLOSE之後初始化幾個SEND,然後開 始RECEIVE,直到接收到對方的CLOSED而RECEIVE失敗爲止。我們假設TCP可以通知用戶連接關閉,即使仍在RECEIVE也可以,這樣用 戶就可以正常關閉了。這樣,TCP可以在連接關閉前可靠地發送數據。下面列舉了三種基本情況:

情況1:本地用戶關閉

這 種情況下,可以建立一個FIN段放入發送隊列。TCP不再接收用戶的SEND指令,TCP進入FIN-WAIT-1狀態。RECEIVE在這種狀態下是允 許的。所有數據段和FIN在未接收到確認以前會一直髮送。當另一個TCP確認FIN,併發送自己的FIN後,本地的TCP可以確認這個FIN了。請注 意,TCP也可以在確認FIN時不返回自己的FIN,直到用戶關閉連接時再返回自己的FIN。

情況2:TCP從網絡上接收到一個FIN

如果在沒有請求的情況下收到FIN,TCP可以返回ACK並通知用戶連接已經關閉。用戶可以以CLOSE響應,TCP在發送完剩下的數據後發送自己的FIN,然後TCP等待對這個FIN的確認,在接收到後,它關閉連接。如果確認超時,可以關閉連接並通知用戶完事。

情況3:雙方同時關閉

雙方同時關閉會導致交換FIN。雙方會在收到對自己FIN的確認後關閉連接。

通常的關閉順序
同時關閉

 

3.6. 優先和安全

TCP 的操作必須在兩個優先級相同的端口間進行。TCP使用的優先和安全參數在IP協議中定義。我們這裏所說的安全/間隔就是指的IP中定義的優先,用戶組和處 理規定。如果不符合則發送RST。這些內容請大家看上一節中的敘述。TCP在操作過程中也會檢查接收數據段的優先級,還可以在操作中提高優先級。雖然運行 在無安全環境中,主機也必須能夠處理安全參數。

3.7. 數據通信

建 立了連接以後就是傳送數據了,TCP通過重新傳送保證每個數據段到達對方,因爲有了重新傳送,所以對方可能接收到兩個相同的包,那就必須根據內部的序列號 判斷哪個數據段是可以接收的。發送方通過使用SND.NXT跟蹤下一個要發送的數據段,而接收方則跟蹤RCV.NXT來知道下一個要接收的數據段。發送方 要還未確認的最老的序列號保存於SND.UNA。

當 發送方形成數據段併發送它後SND.NXT增大;當接收方接收到數據段後RCV.NXT增大併發送確認;當發送方接收到確認後SND.UNA增大。它們三 者在不同的時間增大,這是因爲傳送時延造成的。而增大多少則由數據段中數據的大小決定。注意:連接進入ESTABLISHED狀態後,所有的段必須包括當 前的確認信息。而CLOSE用戶操作的性質類型於推操作,這和在接收到的數據段中的FIN標記一樣。

重傳超時

因 爲網絡中有不類型的網絡,而使用TCP的範圍又很廣,因此重傳超時必須動態決定。下面給出一個例子,通過例子可以看出確定重傳超時的過程。下面有兩個變量 說明時延的問題,一個是環路時間(RTT),它是由一個序列碼得到的,這個序列碼在發送時給出,在接收到確認時被覆蓋;另一個平滑環路時間(SRTT):

SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)

通過上面的式子,可以得到重傳超時(RTO):

RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

其中UBOUND是超時的上界(如1分鐘),LBOUND是超時的下界(如1秒鐘),ALPHA是平滑因子(如0.8到0.9),BETA是延時變量(如1.3到2.0)。

傳送緊急消息

TCP 的緊急機制是允許發送者使接收者接收一些緊急消息,並讓接收方在接收到這一消息後立刻通知用戶。這種機制是在數據流是加入一個點,指出這是緊急數據的結束 點,當接收方要接收到這個點之前,它會通知用戶進入緊急狀態,在接收到這個點的數據後,它會通知用戶進入通常狀態。如果這個緊急點在用戶進入緊急狀態時更 新,這個更新必須對用戶透明。

應用一個緊急域的方法可以達到上述目的,而URG控制標記則指明緊急域是否被使用,而且在數據段中必須加入指示緊急點的序列號,如果沒有這個標記則說明沒有緊急點。如果需要發送緊急數據,發送方必須起碼發送一個字節。

管理窗口

如 果我們學習過網絡基礎,我們一定知道有一個窗口協議,TCP中每個數據段都包括下一個希望接收到的序列號。窗口比較大會提高傳送速度,如果傳送過來的數據 超過的窗口大小,數據會被拋棄。這樣會加重網絡負擔。如果TCP開始時的接收窗口比較大,而到最後這個窗口縮小了,這種作法可不是好辦法。對於健壯的 TCP來說,最好不要自己縮小窗口,但要做要準備對方的TCP縮小窗口。

即 使發送窗口大小爲零,發送TCP也必須做好準備接收數據並且可以發送至少一位新數據。即使接收窗口爲零,發送方也會重發,重發間的間隔時間一般爲兩分鐘。 當接收TCP的接收窗口爲零時,在它接收到數據時,它依然會返回確認,其中包括希望收到的序列碼和當前窗口大小(爲零)。

發 送TCP將數據打成符合當前大小的包,但在重新傳送隊列中可能重新打包。這種重新打包並不是必須的,但是會很有好處。如果一個連接只有單向數據流,窗口信 息可以在確認信息中得到,這些的序列號都是一個,因此沒法區別出誰先誰後。但這並不是一個嚴重的問題。改進的方法是在數據段中加入最新的期待序列號,這樣 就可以區別出誰先誰後了。窗口管理對傳送效率有很大影響,下面是一些建議:

當 使用更少的大數據而要獲得高效率時分配非常小的窗口使數據以許多小段傳送。對於發送者來說要避免在發送小數據段時必須等到窗口大到一定程度再進行。確認不 要被延時。接收窗口如果大小爲零時,要傳送的數據段可能被分成越來越小的段。如果發送TCP僅僅發送窗口允許大小的數據段,可能這些數據段會被換成比這個 段大(或者小)的數據段發送。接收方時不時地進行窗口大小調整會使大數據段分爲小段,而不成對。儘量把小的窗口合成大的窗口。

3.8. 接口

網絡協議的分層結構決定了TCP有兩個接口,向上的用戶接口和下面的接口。對下層接收口的描述不是很清楚,這個工作由下層協議來描述,但是這裏我們也描述一些TCP要使用要的參數。

用戶/TCP接口

下面描述一些TCP命令,這些命令是功能性的,因各系統不同具體實現可能不同。最後,我們必須清楚,不同的TCP實現可能有不同的用戶接口,但是有一些功能是最基本的,本節描述的功能便是這個基本功能集。

TCP用戶命令

下 面定義的命令與其它高級語言中定義的方法差不多,但有些陷井操作需要的,如SVC,UUO,EMT。下面只是一個功能介紹,具體實現可能大爲不同,如有些 系統會把這裏的多條命令結合成一條命令使用。爲了實現通信功能,TCP不能只會接收命令,而且能夠返回消息給它服務的進程,這些消息包括:

(a) 關於連接的通常消息,如中斷,遠程關閉等等;

(b) 對用戶命令做出應答,是成功還是失敗。

Open

格式:OPEN (local port, foreign socket, active/passive [, timeout] [, precedence] [, security/compartment] [, options]) -> local connection name

我們假定本地TCP注意到它所服務的進程標記,而且將檢查進程的認證。因實現不同,本地網絡和源地址的TCP認證可能由TCP進行也可以由下層協議進行(如IP協議)。這些考慮主要是基於安全性的考慮。

如 果active/passive標記設置爲passive,TCP會檢測到達的連接請求,它是被動的,被動的連接可以有一個完全指定的外套接字用於等待特 定的連接或未指定的外套接字來等待任何呼叫。一個完全定義的被動呼叫可以通過執行一系列的SEND命令而變爲主動的。創建一個傳輸控制塊(TCB),其中 一部分參數是由OPEN命令參數而來。在主動OPEN時,TCP會立刻開始同步連接。如果給出參數timeout,會允許呼叫者應用於所有TCP數據。如 果在timeout規定的時間內還未把數據送到目的地,就關閉連接,一般的默認值是5分鐘。

參 數precedence或security/compartment是用於規範用戶對某一連接的安全性的,如果沒有指定則使用默認值。TCP會匹配這兩個 參數,只有在security/compartment一致而且接收到的precedence小於等於要求的precedence時纔會打開連接。當連接 的precedence大於要求的值時,從接收到的數據段中取得這一值,並在連接中一直使用這個比較大的值。具體實現時可以給用戶權力控制 precedence的決定過程。例如,用戶可以要求precedence必須安全一致,或在提升precedence時要通知用戶。

本地連接名(local connection name)由TCP返回用戶,它可以代替<本地套接字,外套接字>標記一個連接。

Send

格式:SEND (local connection name, buffer address, byte count, PUSH flag, URGENT flag [,timeout])

這 個命令使指定緩衝區內的數據發向指定連接,如果連接未打開則返回一個錯誤,一些實現中可能在打開連接以前調用SEND,由SEND自動打開連接。如果設置 了PUSH標記,數據必須立刻傳送給接收者,而且PUSH位在緩衝區中的最後一個段是必須設置。如果沒有設置,此數據段就會因爲效率的考慮而和下一個 SEND命令傳送的數據一起傳送。如果設置了URGENT標記,那就必須象上面所述的一樣緊急處理。傳送方設置的URGENT數目不一定非要等於通知接收 方用戶的次數。

如 果在OPEN中沒有指定外套接字,那緩衝區數據會被髮送到隱式外套接字。未使用外套接字打開連接的用戶仍然可以SEND,而不用知道外套接字地址。但是, 如果在指定外套接字前進行SEND,則會出錯。用戶可以使用STATUES確定連接狀態。如果指定了timeout,對於當前連接的當前用戶 timeout會更改爲新的連接。

最 簡單的實現方法就是在沒有把數據發送完畢以前,SEND不把控制權交給發送進程,但是這樣可能會造成死鎖(例如,雙方都試圖SEND,而不準備 RECEIVE),效率也不好。好的實現方法是在發送一段數據後返回控制權給發送進程,當然如果能夠多個SEND同時發送,那更好不過了。有多個SEND 需要服務時一般採取先來先服務。

對 於本地來說,我們現在假定的是SEND在發送數據後會產生類似中斷的東西告訴發送進程數據發送的情況;當然SEND也可以在發送完後立刻告知進行數據發送 的情況。我們可以樂觀地認爲發送是成功的,如果發送失敗,連接會因爲超時而關閉。在實現中,即使是同步返回情況,也需要一些異步信號,但這些異步信號是用 於處理連接的,而不是用於處理數據發送的。既然允許多個SEND同時工作,因此有必要區別返回的信息是哪一個SEND的。具體的情況下面會討論到。

Receive

格式: RECEIVE (local connection name, buffer address, byte count) -> byte count, urgent flag, push flag

此 命令分配一個接收緩衝區給指定的連接。如果下面不是一個OPEN命令或者此調用進行未被授權使用此連接返回錯誤。最簡單的實現方法是在緩衝區沒有填充完以 前不返回控制權,但這樣可能會造成嚴重的死鎖。更復雜的實現方法允許同時存在多個RECEIVE,這樣會提高效率。這樣是在控制複雜的情況下取得了高效 率。

如 果在PUSH之前的數據已經填滿緩衝區,那麼對於RECEIVE的響應中就不用設置PUSH位了。緩衝區會容納儘可能多的數據,如果在緩衝區填充滿以前看 到PUSH位,將返回緩衝區中的數據並設置PUSH。在處理緊急狀態時,如果有URGENT標記,還有緊急數據;如果沒有URGENT標記了,就返回所有 緊急數據,用戶也離開緊急狀態。請注意,在緊急指針指向的點以後的那些數據不能和緊急數據一起返回,即使它們在同一個緩衝區內,當然,如果用戶指定要這麼 做例外。

爲了區別多個RECEIVE並保證緩衝區不被充滿,返回的數據中也要包括緩衝區指針和一個計數器指明現在接收了多少數據。RECEIVE可以有自己專用了緩衝區,也可以和用戶共享一個緩衝區。

Close

格式: CLOSE (本地連接名)

此 命令關閉連接,如果連接未打開,或未授權可以關閉連接返回錯誤。在關閉的時候應該注意正常關閉,讓所有的發送都發送完數據,也可以如上所說在CLOSE後 面加上幾個SEND,這就要求用戶在收到CLOSE後仍然要進行接收。因此,CLOSE意指“我沒有更多的數據要發了”,並不代表“我不再發送任何數據 了”。關閉方也可能在超時前不能發出所有數據,這種情況下,要由CLOSE轉入ABORT狀態。用戶可以自己決定在任何時間關閉連接,也可以根據TCP返 回的提示關閉。因此關閉操作要和外TCP進行通信,因此在關閉狀態可能要呆一會兒,在CLOSE沒有返回前調用打開,會返回錯誤。

Status

格式: STATUS (本地連接名) -> status data

這 個命令和具體的實現有關,而且有可能會有負作用。返回的信息通常來自有連接相關的TCB。返回的數據塊中包括下面的信息:本地套接字,外套接字,本地連接 名,接收窗口,發送窗口,連接狀態,等待確認的緩衝區數,等待接收的緩衝區數,緊急狀態,優先級,security/compartment和傳輸超時。 因此實現不同,所以上述數據項中可能有幾項沒有意義或根本不存在。如果調用進程沒有被授權使用這一連接,返回錯誤。這一點會防止未被授權的進程獲得連接狀 態。

Abort

格式: ABORT (本地連接名)

此命令中止所有SEND和RECEIVE,刪除TCB,將發送特殊的RESERT信息到對方TCP。具體的返回信息會因實現不同而不同。

TCP到用戶信息

假定操作系統提供一種可以使TCP異步傳送信息到用戶程序的機制。當TCP確實通知用戶程序時會返回一些特定的信息。通常在這些信息中也會有錯誤信息,在其它情況下會有關於完成SEND或RECEIVE或其它用戶調用的相關信息。會提供下面的幾種消息:

本地連接名 都提供

響應串 都提供

緩衝區地址 發送和接收

字節記數 接收

Push標記 接收

Urgent標記 接收

 

TCP和下層接口

TCP實際上調用下層服務才能在網絡上傳輸數據,在互聯網上我們通常認爲TCP的下層是IP協議。如果下層是IP層,它提供一些類於服務類型和生存時間的參數。TCP使用這些參數的如下設置:

Type of Service = Precedence: routine, Delay: normal, Throughput: normal, Reliability: normal;或是數字00000000。

Time to Live = 一分鐘,或是數字00111100。

請注意:假定的最大數據段的生存時間爲2分鐘,這裏人爲指定爲1分鐘。

如 果下層是IP而且使用源地址路由,接口必須允許路由信息的通信。這對建立連接和進行路由是十分重要的。當然也可以不使用IP協議作爲TCP的底層協議,但 無論下層協議是什麼,都必須提供源地址,目的地址和協議域,以及一些決定TCP長度的域,總之一句話,要能夠提供類似於IP的功能。

3.9. 事件處理

下 面說明的過程是可能的實現,其它實現和本例的過程可能有一點點不同,但只在細節,而決不在結果。TCP的活動可以總結爲對事件的響應。事件可以分爲三類: 用戶調用,接收數據段和超時。下面描述的是TCP對具體事件的響應,在許多情況下,相關的動作(響應)要和連接狀態相關。

用戶調用的有:

OPEN

SEND

RECEIVE

CLOSE

ABORT

STATUS

接收數據段的有:

SEGMENT ARRIVES

超時的有:

USER TIMEOUT

RETRANSMISSION TIMEOUT

TIME-WAIT TIMEOUT

TCP對用戶的響應可能是立即的也可能是延時的。錯誤信息以字符串的形式給出。下面就是一個具體的錯誤信息:error: connection not open。另外,請記住序列號空間大小爲2的32次方。處理數據段的順序爲首先接收,再檢查序列號,如果是要接收的就放入接收隊列。另外,沒有說明狀態轉移時,TCP保持原來的狀態。

OPEN調用

CLOSED狀態

創 建新的TCB保存連接狀態信息,填充本地套接字標記,外套接字,優先級,security/compartment和用戶超時信息。注意一部分外套接字在 被動OPEN中可能未說明。如果是主動的,而外套接字未指定,返回"error: foreign socket unspecified";如果是主動的,而外套接字指定了,發送一個SYN數據段。選擇初始發送序列號ISS。SYN數據段的格式如 下<SEQ=ISS><CTL=SYN>,設置SND.UNA爲ISS,SND.NXT爲ISS+1,進行SYN-SENT狀 態,然後返回。

如果調用者不能訪問指定的本地套接字,返回"error: connection illegal for this process"。如果沒有空間接收新的連接,返回"error: insufficient resources"。

LISTEN狀態

如 果處於主動狀態,指定了外套接字,可以將連接從被動改爲主動,並選擇ISS。發送一個SYN數據段,設置SND.UNA爲ISS,SND.NXT爲 ISS+1。進入SYN-SENT狀態。和SEND一起的數據可以和SYN數據段一起發送,也可以在進入ESTABLISHED狀態後發送。如果沒有空間 接收請求,返回"error: insufficient resources"。如果未指定外套接字,返回"error: foreign socket unspecified"。如果處於以下狀態:SYN-SENT狀態,SYN-RECEIVED,ESTABLISHED狀態,FIN-WAIT-1狀 態,FIN-WAIT-2狀態,CLOSE-WAIT狀態,CLOSING狀態,LAST-ACK狀態或TIME-WAIT狀態時返回"error: connection already exists"。

SEND調用

CLOSED狀態

如果用戶無權訪問連接,返回"error: connection illegal for this process"。否則返回"error: connection does not exist"。

LISTEN狀態

如 果指定了外套接字,可以將連接從被被動改爲主動,選擇一個ISS。發送SYN數據段,設置SND.UNA爲ISS,SND.NXT爲ISS+1。進入 SYN-SENT狀態。和SEND一起的數據可以和SYN數據段一起發送,也可以在進入ESTABLISHED狀態後發送。如果沒有空間接收請求,返 回"error: insufficient resources",如果未指定外套接字,則返回"error: foreign socket unspecified"。

SYN-SENT狀態和SYN-RECEIVED時

在進入ESTABLISHED狀態後將需要傳送的數據加入隊列。如果隊列已無空間,則返回"error: insufficient resources"。

ESTABLISHED狀態和CLOSE-WAIT狀態

將緩衝區分段,發送緩衝區數據,並使它帶有確認值RCV.NXT。如果沒有空間保存緩衝區,則返回"error: insufficient resources"。如果設置了緊急標記,那麼SND.UP <- SND.NXT-1,並設置緊急指針指向發送數據段中的相應位置。

在FIN-WAIT-1,FIN-WAIT-2,CLOSING,LAST-ACK和TIME-WAIT狀態時返回"error: connection closing",不理會請求。

RECEIVE調用

CLOSED狀態

如果用戶沒有權利訪問這個連接,返回"error: connection illegal for this process"。如果有權利,則返回"error: connection does not exist"。

在LISTEN,SYN-SENT和SYN-RECEIVED狀態下

在進入ESTABLISHED狀態後,將數據放入隊列準備處理。如果隊列中沒有空間,返回"error: insufficient resources"。

在ESTABLISHED,FIN-WAIT-1和FIN-WAIT-2狀態下

如果沒有接收到足夠的數據段滿足請求,將請求放入隊列。如果隊列中沒有空間記錄RECEIVE,返回"error: insufficient resources"。重新將接收數據段放入接收緩衝區,並返回給用戶。在這種情況下,可以設置PUSH標記。如果RCV.UP比現在傳送給用戶的要大,通知用戶有緊急數據。

CLOSE-WAIT狀態

因爲遠方TCP已經發送了FIN,RECEIVE必須由現在在緩衝區但還未傳送給用戶的數據滿足。如果沒有數據了,RECEIVE會得到"error: connection closing"的響應。

在CLOSING,LAST-ACK和TIME-WAIT狀態下返回"error: connection closing".

 

CLOSE 調用

CLOSED狀態

如果用戶沒有權利訪問這個連接,返回"error: connection illegal for this process"。如果有權利,則返回"error: connection does not exist"。

LISTEN狀態

存在着的任何RECEIVE都會返回"error: closing"的響應。刪除TCB,然後進入CLOSED狀態並返回。

SYN-SENT狀態

刪除TCB並返回"error: closing"響應給任何在隊列中的SEND或RECEIVE。

SYN-RECEIVED狀態

如果沒有發送SEND,而且沒有等待發送的數據了,則形成併發送FIN數據段,進入FIN-WAIT-1狀態;否則在進入ESTABLISHED狀態後將命令保存在隊列中等待處理。

ESTABLISHED狀態

將這一命令保存在隊列中,直到所有正在進行的SEND完成後,形成併發送FIN數據段,然後進入FIN-WAIT-1狀態。

在FIN-WAIT-1和FIN-WAIT-2狀態下

嚴格地說,這將會是個錯誤,會收到"error: connection closing"。只要第二個FIN沒有發出,也可以收到一個"ok"響應。

CLOSE-WAIT狀態

直到所有SEND命令完成後再將這個請求放入隊列;然後發送FIN數據段,進入CLOSING狀態。

在CLOSING,LAST-ACK和TIME-WAIT狀態下返回"error: connection closing"。

 

ABORT調用

CLOSED狀態

如果用戶沒有權利訪問這個連接,返回"error: connection illegal for this process"。如果有權利,則返回"error: connection does not exist"。

LISTEN狀態

任何存在的RECEIVE都應該返回"error: connection reset",刪除TCB,進入CLOSED狀態,然後返回。

SYN-SENT狀態

對於所有在隊列中的SEND和RECEIVE,都返回"connection reset",刪除TCB,進入CLOSED狀態,然後返回。

在SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2和CLOSE-WAIT狀態

發送數據段<SEQ=SND.NXT><CTL=RST>,對於所有在隊列中的SEND和RECEIVE,都返回"connection reset",;所有要進行傳送或重新傳送的數據段立刻進行,刪除TCB,進入CLOSED狀態,然後返回。

在CLOSING,LAST-ACK和TIME-WAIT狀態下返回"ok",刪除TCB,進入CLOSED狀態,然後返回。

 

STATUS 調用

CLOSED狀態

如果用戶沒有權利訪問這個連接,返回"error: connection illegal for this process"。如果有權利,則返回"error: connection does not exist"。

LISTEN狀態

返回"state = LISTEN"和TCB指針。

SYN-SENT狀態

返回"state = SYN-SEND"和TCB指針。

SYN-RECEIVED狀態

返回"state = SYN-RECEIVED"和TCB指針。

ESTABLISHED狀態

返回"state = ESTABLISHED"和TCB指針。

FIN-WAIT-1狀態

返回"state = FIN-WAIT-1"和TCB指針。

FIN-WAIT-2狀態

返回"state = FIN-WAIT-2"和TCB指針。

CLOSE-WAIT狀態

返回"state = CLOSE-WAIT"和TCB指針。

CLOSING狀態

返回"state = CLOSING"和TCB指針。

LAST-ACK狀態

返回"state = LAST-ACK"和TCB指針。

TIME-WAIT狀態

返回"state = TIME-WAIT"和TCB指針。

接收到數據段

如 果處於CLOSED狀態,所有到達的數據都被拋棄。包括RST的也不例外。如果接收到的數據段不包括RST,則返回一個RST。對於發送TCP要選擇它可 以認知的確認和序列號。如果關閉ACK位,則序列號爲0,格式如下:<SEQ=0><ACK=SEG.SEQ+SEG.LEN& gt;<CTL=RST,ACK>如果打開ACK位,格式如下:<SEQ=SEG.ACK><CTL=RST>。

如果處於LISTEN狀態,首先檢查RST,接收到的RST應該被忽略。下來應該檢查ACK,如果連接處於LISTEN狀態下,任何ACK都是錯誤的,應該返回RST段,它的格式如下:<SEQ=SEG.ACK><CTL=RST>。

第 三步應該檢查SYN,如果SYN位設置了,檢查安全性,如果接收數據段中的security/compartment與TCB中的不能匹配,返回RST 段,格式如下:<SEQ=SEG.ACK><CTL=RST>。在SEG.PRC大於TCB.PRC的情況下,如果用戶和系統允 許,則設置TCB.PRC<-SEG.PRC,如果用戶和系統不允許,發送RST段並返回。其格式如下:<SEQ=SEG.ACK>& lt;CTL=RST>。如果SEG.PRC小於TCB.PRC,繼續進行。設置RCV.NXT爲SEG.SEQ+1,IRS爲SEG.SEQ中的 值,其它控制信息和數據以後處理。應該選擇ISS,併發送SYN段,其格式如下:<SEQ=ISS><ACK=RCV.NXT& gt;<CTL=SYN,ACK>。SND.NXT設置爲ISS+1,SND.UNA設置爲ISS的值,連接狀態改爲SYN- RECEIVED。其它控制信息和數據將在SYN-RECEIVED狀態中處理,但以於SYN和ACK的處理不應該重複。如果未完全指定LISTEN域, 現在就應該指定了。

第四步是處理其它數據和控制信息。其它的數據段(不包括SYN)應該有ACK,因此不要對它進行ACK處理。如果接收到RST段,則它是非法的。如果確實接收到了,拋棄它,直接返回。

如 果處於SYN-SENT狀態。首先檢查ACK位,如果設置了ACK位,而且SEG.ACK =< ISS或SEG.ACK > SND.NXT,發送RST。(除非設置了RST位,如果這樣,拋棄這個數據段直接返回)。RST段的格式如下:<SEQ=SEG.ACK& gt;<CTL=RST>。不用理會收到的數據段,直接返回。如果SND.UNA =< SEG.ACK =< SND.NXT,那麼ACK是可以接受的。

下一步檢查RST位。在設置了RST的情況下,如果ACK是可以接受的,通知用戶"error: connection reset",拋棄接收到的數據段並進入CLOSED狀態,刪除TCB並返回。如果ACK不能接受,拋棄數據段返回。

第 三步檢查安全性和優先級。如果security/compartment和TCB中的security/compartment不匹配,發送RST段。在 發送RST段的時候,如果已經有ACK了,格式爲:<SEQ=SEG.ACK><CTL=RST>,如果沒有ACK,格式爲& lt;SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>。在沒有發現不匹配的時候,如 果有ACK,段中的優先級必須和TCB中的匹配,如果不匹配,發送RST,其格式爲:<SEQ=SEG.ACK><CTL=RST& gt;;如果未發現不匹配,而且沒有ACK的情況下,如果接收到的數據段中的優先級高於TCB中的優先級,在得到用戶和系統許可的情況下可以將TCB中的 優先級升高,如果用戶或系統不許可,提高prec,然後以如下格式發送RST:<SEQ=0>< ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>,如果接收數據段中的優先級小於TCB中的優先級則繼續。在發送 了RST後,拋棄數據段並返回。

第 四步檢查SYN位。只有在ACK位是合法或沒有ACK而且數據段中不包括RST的情況下才能進行這一步。如果設置了SYN位,而且 security/compartment和優先級合法,那麼RCV.NXT設爲SEG.SEQ+1,IRS設爲SEG.SEQ。SND.UNA應該增加 和SEG.ACK相等,在重新發送隊列中原來等待確認而現在已經被確認的數據段也被清除。如果SND.UNA > ISS,改變連接狀態爲ESTABLISHED,形成下面格式的ACK段併發送: <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>。隊列中用於發送的數據的控制信息也一起 發送,如果段中沒有數據或控制信息,則進行第六步,否則返回。如果SND.UNA > ISS不成立,形成SYN,ACK段,格式如下:<SEQ=ISS><ACK=RCV.NXT>< CTL=SYN,ACK>,併發送它。如果在段中沒有數據或控制信息,待進入ESTABLISHED狀態後再進行處理。

第五步,如果SYN或RST位沒有設置,拋棄數據段返回。

下來我們來看看其它狀態。首先應該檢查序列號。在下面狀態下

SYN-RECEIVED狀態

ESTABLISHED狀態

FIN-WAIT-1狀態

FIN-WAIT-2狀態

CLOSE-WAIT狀態

CLOSING狀態

LAST-ACK狀態

TIME-WAIT狀態

段按順序處理,首先拋棄重複的段,對於以後的處理要根據SEG.SEQ的大小進行。如果有的段內的新老內容重疊在一起,那隻用處理新的那一部分。下面是對接收到的數據的可接受性測試中的四種情況:

段長度

接收窗口

測試

0

0

SEG.SEQ = RCV.NXT

0

>0

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND

>0

0

不接受

>0

>0

RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND或RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

如 果RCV.WND = 0,除了合法的ACK,URG和RST段外拒絕其它的數據段。如果接收到的數據段不可接受,應該返回一個應答,格式如 下:<SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>。在發送完應答後,拋棄不可接受的數 據段,然後返回。

第二步檢查RST位。

如果處於SYN-RECEIVED 狀態時,而且處於設置了RST的情況下,如果連接以被動OPEN開始,將連接返回到LISTEN狀態,不需要通知用戶;如果連接以主動OPEN打開,拒絕連接,並通知用戶"connection refused"。在上面任何一種情況下,所有在重發隊列中的數據都要刪除。在主動OPEN的那種情況下,進入CLOSED狀態,刪除TCB然後返回。

如果處於ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2或CLOSE-WAIT狀態時,而且RST已經設置,那麼任何存在的RECEIVE和SEND都會收到"reset"。所有隊列中的數據段都應該立即發送。用戶也會收到"connection reset"。進入CLOSED狀態,刪除TCB並返回。

如果處於CLOSING狀態,LAST-ACK狀態或TIME-WAIT狀態,而且RST已經設置,進入CLOSED狀態,刪除TCB並返回。

檢查安全和優先級

在SYN-RECEIVED狀態下

如果段中的security/compartment和優先級和TCB中的不匹配,發送RST並返回。

在ESTABLISHED狀態下

如果段中的security/compartment和優先級和TCB中的不匹配,發送RST ,所有存在的RECEIVE和SEND接收到"reset",立即發送所有隊列中的數據段,用戶接收到"connection reset"。進入CLOSED狀態,刪除TCB並返回。

第四步檢查SYN位,如果連接處於以下狀態

SYN-RECEIVED

ESTABLISHED狀態

FIN-WAIT STATE-1

FIN-WAIT STATE-2

CLOSE-WAIT狀態

CLOSING狀態

LAST-ACK狀態

TIME-WAIT狀態

如果SYN在窗口中就是錯誤,發送RST,任何存在的RECEIVE和SEND收到"reset",所有在隊列中的數據段立即發送,用戶也接收到"connection reset",進入CLOSED狀態,刪除TCB並返回。如果SYN未在窗口中,這一步不會發生。

第五步檢查ACK域

如果ACK位關閉,拋棄數據段返回。如果ACK域打開的情況下,如果連接處於

SYN-RECEIVED狀態時

如果SND.UNA =< SEG.ACK =< SND.NXT,進入ESTABLISHED狀態。如果段的確認消息不可接受,形成如下形式的RST併發送:

<SEQ=SEG.ACK><CTL=RST>

ESTABLISHED狀態時

如果SND.UNA < SEG.ACK =< SND.NXT,設置SND.UNA <- SEG.ACK。因此而對重新傳送隊列中數據段的確認也帶來了對這些數據段的刪除。用戶應該接收對緩衝區的主動確認,如果ACK是重複的(SEG.ACK < SND.UNA)可以忽略這個ACK。如果ACK確認了還未發送的東西(SEG.ACK > SND.NXT),那麼可以發送ACK,拋棄數據段並返回。

如果SND.UNA < SEG.ACK =< SND.NXT,應該更新發送窗口。如果(SND.WL1 < SEG.SEQ)或(SND.WL1 = SEG.SEQ且SND.WL2 =< SEG.ACK),設置SND.WND <- SEG.WND,SND.WL1 <- SEG.SEQ和SND.WL2 <- SEG.ACK。

FIN-WAIT-1狀態

除了對於ESTABLISHED狀態的處理外,如果確定了FIN,則進入FIN-WAIT-2狀態並在這個狀態下繼續處理。

FIN-WAIT-2狀態時

除了對於ESTABLISHED狀態的處理外,如果重新發送隊列爲空,確認用戶的CLOSE,但不刪除TCB。

CLOSE-WAIT狀態時

同ESTABLISHED狀態的處理。

CLOSING狀態時

除了對於ESTABLISHED狀態的處理外,如果確定了FIN,則進入TIME-WAIT狀態,如果未確認,則忽略這個段。

LAST-ACK狀態時

在此狀態下唯一可能發生的就是重要發送遠程FIN。如果確認了FIN,則刪除TCB,進入CLOSED狀態並返回。

TIME-WAIT狀態時

在此狀態下唯一可能發生的就是重要發送遠程FIN。確認它,並重新開始2 MSL超時。

第 六步檢查URG位,如果連接處於ESTABLISHED狀態,FIN-WAIT-1狀態或FIN-WAIT-2 狀態,而且URG位被設置,那麼RCV.UP <- max(RCV.UP,SEG.UP),通知用戶遠方有緊急數據,如果用戶已經處於緊急狀態,不用再多嘴了。連接在此時不會處於CLOSE-WAIT狀 態,CLOSING狀態,LAST-ACK狀態或TIME-WAIT狀態,因爲還沒有從遠方獲得FIN。

第七步處理段數據

處 於ESTABLISHED狀態,FIN-WAIT-1 狀態或FIN-WAIT-2 狀態時,在進入ESTABLISHED狀態後纔可能向用戶接收緩衝區傳送數據。段中的數據可以移向緩衝區,直到緩衝區滿或段爲空爲止。如果段爲空並帶有標 記PUSH,在返回緩衝區數據後要通知用戶接收到PUSH。TCP傳送數據時必須對接收到的數據也發給確認。前面已經說過對RCV.NXT和 RCV.WND的處理,這裏不再多說了。發送的確認有如下格式:<SEQ=SND.NXT><ACK=RCV.NXT>< CTL=ACK>。

而CLOSE-WAIT狀態,CLOSING狀態,LAST-ACK狀態或TIME-WAIT 狀態根本不會發生,因爲還未從遠程TCP接收到FIN。

第八步檢查FIN位。如果狀態是CLOSED,LISTEN或SYN-SENT,不要處理FIN,因爲此時的SEG.SEQ不會的意義,應該直接拋棄數據段返回。如果設置了FIN位,通知用戶"connection closing"中止所有正在進行的RECEIVE,增加RCV.NXT超過FIN,對FIN發送確認。

如果此時處於:SYN-RECEIVED狀態或ESTABLISHED狀態進入CLOSE-WAIT狀態。

如果此時處於FIN-WAIT-1 狀態

如果確定了FIN段,那麼進入TIME-WAIT狀態,打開time-wait計數器,關閉其它計數器,如果沒有確認,進入CLOSING狀態。

如果此時處於FIN-WAIT-2狀態

進入TIME-WAIT狀態,關閉其它計數器,打開time-wait計數器。

如果此時處於CLOSE-WAIT狀態或CLOSING狀態或LAST-ACK狀態,保留各自原來的狀態。

如果此時處於TIME-WAIT狀態,保持在TIME-WAIT狀態。重新開始2 MSL time-wait超時。

 

用戶超時

對於任何一種狀態,用戶超時的情況下,發送隊列中的數據,返回"error: connection aborted due to user timeout",刪除TCB,進行CLOSED狀態並返回。

 

重發超時

在任何狀態下,如果重發隊列中的數據段發送超時,將它再次放到隊列首部重新發送。

 

TIME-WAIT超時

如果time-wait超時,刪除TCB,進行CLOSE狀態並返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章