?? tcp協議規范.htm
字號:
<P align=justify>SEG.SEQ = 一個數據段的第一個序列號;</P>
<P align=justify>SEG.LEN = 數據段中包括的字節數;</P>
<P align=justify>SEG.SEQ+SEG.LEN-1 = 數據段的最后一個序列號。</P>
<P align=justify>請注意下面的關系:</P>
<P align=justify>SND.UNA < SEG.ACK =< SND.NXT</P>
<P align=justify>如果一個數據段的序列號小于等于確認號的值,那么整個數據段就被確認了。而在接收數據時下面的比較操作是必須的:</P>
<P align=justify>RCV.NXT = 期待的序列號和接收窗口的最低沿;</P>
<P align=justify>RCV.NXT+RCV.WND-1 = 最后一個序列號和接收窗口的最高沿; </P>
<P align=justify>SEG.SEQ = 接收到的第一個序列號;</P>
<P align=justify>SEG.SEQ+SEG.LEN-1 = 接收到的最后一個序列號;</P>
<P align=justify> </P>
<P align=justify>上面幾個量有如下關系:</P>
<P align=justify>RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND 或 RCV.NXT
=< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND</P>
<P
align=justify>測試的第一部分是檢查數據段的開始部分是否在接收窗口中,第二部分是檢查數據段的結束部分是否也在接收窗口內;上面兩個檢查通過任何一個就說明它包括窗口要求的數據。實際中的情況會更復雜一些,因為有零窗口和零數據段長,因此我們有下面四種情況:
</P></FONT>
<TABLE cellSpacing=1 cellPadding=7 width="100%" border=1>
<TBODY>
<TR>
<TD vAlign=top width="13%"><FONT face=宋體 size=3>
<P align=justify>段長度</FONT></P></TD>
<TD vAlign=top width="16%">
<BLOCKQUOTE><FONT face=宋體 size=3>
<P align=justify>接收窗口</FONT></P></BLOCKQUOTE></TD>
<TD vAlign=top width="71%"><FONT face=宋體 size=3>
<P align=justify>測試</FONT></P></TD></TR>
<TR>
<TD vAlign=top width="13%"><FONT face=宋體 size=3>
<P align=justify>0</FONT></P></TD>
<TD vAlign=top width="16%">
<BLOCKQUOTE><FONT face=宋體 size=3>
<P align=justify>0</FONT></P></BLOCKQUOTE></TD>
<TD vAlign=top width="71%"><FONT face=宋體 size=3>
<P align=justify>SEG.SEQ = RCV.NXT</FONT></P></TD></TR>
<TR>
<TD vAlign=top width="13%"><FONT face=宋體 size=3>
<P align=justify>0</FONT></P></TD>
<TD vAlign=top width="16%">
<BLOCKQUOTE><FONT face=宋體 size=3>
<P align=justify>>0</FONT></P></BLOCKQUOTE></TD>
<TD vAlign=top width="71%"><FONT face=宋體 size=3>
<P align=justify>RCV.NXT =< SEG.SEQ <
RCV.NXT+RCV.WND</FONT></P></TD></TR>
<TR>
<TD vAlign=top width="13%"><FONT face=宋體 size=3>
<P align=justify>>0</FONT></P></TD>
<TD vAlign=top width="16%">
<BLOCKQUOTE><FONT face=宋體 size=3>
<P align=justify>0</FONT></P></BLOCKQUOTE></TD>
<TD vAlign=top width="71%"><FONT face=宋體 size=3>
<P align=justify>不可接受</FONT></P></TD></TR>
<TR>
<TD vAlign=top width="13%"><FONT face=宋體 size=3>
<P align=justify>>0</FONT></P></TD>
<TD vAlign=top width="16%">
<BLOCKQUOTE><FONT face=宋體 size=3>
<P align=justify>>0</FONT></P></BLOCKQUOTE></TD>
<TD vAlign=top width="71%"><FONT face=宋體 size=3>
<P align=justify>RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND或RCV.NXT
=< SEG.SEQ+SEG.LEN-1 <
RCV.NXT+RCV.WND</FONT></P></TD></TR></TBODY></TABLE><FONT face=宋體 size=3>
<P
align=justify>請注意接收窗口的大小可以為零,在窗口為零時它只用來接收ACK信息,因此對于一個TCP來說,它可以使用零大小窗口在發送數據的同時接收數據。即使接收窗口的大小為零,TCP必須處理所有接收到信息的RST和URG域。</P>
<P
align=justify>我們也應用計數的方式保護了一些特定的控制信息,這是通過隱式地使用一些控制標記使數據段能夠可靠地重新發送(或確認)為達到的。控制信息并不在段數據空間中傳送,因此,我們必須采用隱式指定序列號進行控制。SYN和FIN是需要保護的控制量,這兩個控制量也只在連接打開和關閉時使用。SYN被認為是在第一個實際數據之間的數據,而FIN是最后一個實際數據之后的數據。段長度(SEG.LEN)包括數據和序列號空間,如果出現了SYN,那么SEG.SEQ是SYN的序列號。</P>
<P align=justify>初始序列號選擇</P>
<P
align=justify>協議對于特定連接被重復使用沒有什么限制。連接是由一對套接字定義的。新的連接實例被定義為連接的另一次恢復,這就帶來了問題:TCP如果確定多個數據段是從以前連接的另一次恢復中取得的呢?這個問題在連接迅速打開和關閉,或因為內存原因被關閉然后又迅速建立后顯示特別突出。</P>
<P
align=justify>為了避免混亂,用戶必須避免因此恢復使用某一連接,而使序列號發生混亂。我們必須保證序列號的正確性,即使TCP失敗,根本不知道以前的序列號是什么的情況下也要保證序列號的正確性。當新的連接被創建時,產生一個新的初始序列號(ISN)產生子,它用來選擇一個新的32位ISN。產生子和32位時鐘的低度位字節相關,低位字節的刷新頻率大概是4微秒,因此ISN的循環時間大概是4.55小時。因此我們把網絡包的最長生存時間(MSL)小于4.55小時,因此我們可以認為ISN是唯一的。對于每個連接都有發送序列號和接收序列號,初始發送序列號(ISS)由發送TCP選擇,而初始接收序列號是在連接建立過程中產生的。</P>
<P
align=justify>對于將要連接或初始化的連接,兩個TCP必須和對方的初始序列號同步。這通過交換一個控制位SYN和初始序列號完成。我們把帶有SYN的數據段稱為"SYNs"。同步的獲得過程這里就不重復了,每方必須發送自己的序列號并返回對對方序列號的確認。</P>
<P align=justify>1) A --> B SYN 本方序列號是X </P>
<P align=justify>2) A <-- B ACK 本方序列號被確認</P>
<P align=justify>3) A <-- B SYN 對方序列號是Y </P>
<P align=justify>4) A --> B ACK 確認對方序列號</P>
<P
align=justify>上面的第2步和第3步可以合并,這時可以成為3階段,所以我們可以稱它為三消息握手。這個過程是必須的,因為序列號不和全局時鐘關聯,TCP也可以有不同的機制選擇ISN。接收到第一個SYN的接收方不可能知道這個數據段是不是被延時,除非它記住了在連接上使用的最近的序列號(這通常是不可能的),因此它必須要求發送者確認。</P>
<P
align=justify>為了保證TCP獲得的確認是剛才發送的段產生的,而不是仍然在網絡中的老數據段產生的,因此TCP必須在MSL時間之內保持沉默。在本文中,我們假設MSL=2小時,這是出于工程的需要,如果用戶覺得可以,他可以改變MSL。請注意如果TCP重新初始化,而內存中的序列號正在使用,不需要等待,但必須確認使用的序列號比當前使用的要大。</P>
<P
align=justify>如果一臺主機在未保留任何序列號的情況下失敗,那么它應該在MSL時間之內不發出任何數據段。下面將會這一情況進行說明。TCP的實現可以不遵守這個規定,但是這會造成老數據被當成新數據接收,而新數據被當成老數據拒絕的情況。</P>
<P
align=justify>每當數據段形成并進入輸出隊列,TCP會為它指定序列空間中的一個值。TCP中多復本檢測和序列算法都依賴于這個地址空間,在對方發送或接收之前不會超過2的32次方個包存在于輸出隊列中。所有多余的數據段都會被刪除。如果沒有這個規定,會出現多個數據段被指定同一個序列號的情況,會造成混亂。數據段中序列號的多少和數據段中的字節數一樣多。</P>
<P
align=justify>在通常情況下,TCP保留下一個要發送的序列號和還未確認的最老的序列號,不要在沒有確認的時候就再次使用,這樣會有些風險,也正是因為這樣的目的,所以序列空間很大。對于2M的網絡,要4.5小時來耗盡序列空間,因為一個數據段可能的最大生存時間也不過十幾分之一秒,這就留下了足夠的空間;而在100M的網絡上需要5.4分鐘,雖然少了點,但也可以了。</P>
<P
align=justify>如果在實現TCP時沒有為保存序列號留下空間,那清除多余的包可能就不能實現了,因此推薦這種類型的TCP實現最好在失敗后等待MSL時間,這樣保證多余的包被刪除。這種情況有時候也可能會出現在保留序列號的TCP實現中。如果TCP在選擇一個另一個TCP連接正在使用的序列號時,這臺主機突然失敗了,這就產生了問題。這個問題的實質在于主機不知道它失敗了多久,也不知道多余的復本是不是還在網絡中。</P>
<P
align=justify>處理這種問題的方法是等待MSL時間,如果不這樣就要冒著對方錯誤接收數據的危險,要等待的時間也就稱為“沉默時間”。實現者可以讓用戶選擇是不是等待,但是無論用戶如何也不見得非要等待MSL時間。</P>
<P align=justify>3.4. 建立一個連接</P>
<P
align=justify>建立連接應用的是三消息握手。如果雙方同時都發送SYN也沒有關系,雙方會發現這個SYN中沒有確認,于是就知道了這種情況,通常來說,應該發送一個"reset"段來解決這種情況。三消息握手減少了連接失敗的可能性。下面就是一個例子,在尖括號是的就是數據段中的內容和標記。其它的就不多說了。</P>
<P align=center><IMG height=132 alt=基本三消息同步
src="TCP協議規范.files/TCPDetail-7.gif" width=506></P>
<P align=justify>在第2行,TCP A發送SYN初始化序列號,表示它要使用序列號100;第3行中,TCP
B給出確認,并且期待著A的帶有序列號101的數據段;第4行,TCP
A給出確認,而在第5行,它也給出確認,并發送了一些數據,注意第4行的序列號與第5號的一樣,因為ACK信息不占用序列號空間內的序列號。同時產生請求的情況如下圖所示,只復雜一點。</P>
<P align=center><IMG height=163 alt=同時連接同步
src="TCP協議規范.files/TCPDetail-8.gif" width=507></P>
<P
align=justify>使用三消息握手的主要原因是為了防止使用過期的數據段。為了這個目的,必須引入新的控制消息,RESET。如果接收TCP處理非同步狀態,在接收到RESET后返回到LISTEN狀態。如果TCP處理下面幾種狀態ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT時,放棄連接并通過用戶。我們下面就詳細說明后一種情況。</P>
<P align=center><IMG height=182 alt=過期SYN的恢復
src="TCP協議規范.files/TCPDetail-9.gif" width=507></P>
<P
align=justify>通過上面的例子,我們可以看出TCP連接是如何從過期數據段的干擾下恢復的。請注意第4行和第5行中的RST(RESET信號)。</P>
<P align=justify>半開連接和其它非正常狀態</P>
<P
align=justify>如果一方在未通過另一方的情況下關閉連接,或雙方雖然失敗而不同步的情況我們稱為半開連接狀態。在一方試圖發送數據時連接會自動RESET。然而這種情況畢竟屬于不正常情況。應該做出相應的處理。如果A處的連接已經關閉,B處并不知道。當B希望發送數據到A時,就會收到RESET信號,表示這個TCP連接有誤,要中止當前連接。</P>
<P
align=justify>假設A和B兩個進程相互通信的時候A的TCP發生了失敗,A依靠操作系統支持TCP的存在,通常這種情況下會有恢復機制起作用,當TCP重新恢復的時候,A可能希望從恢復點開始工作。這樣A可能會試圖OPEN連接,然后在這個它認為還是打開的連接上傳送數據,這時A會從本地(也就是A的)TCP上獲得錯誤消息“未打開連接”。A的TCP將發送包括SYN的數據段。下面的例子將顯示這一過程:</P>
<P align=center><IMG height=168 alt=關開連接檢測
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -