tcp要點學習-建立連接
Author : Kevin Lynx
準備:
在這里本文將遵循上一篇文章的風格,只提TCP協議中的要點,這樣我覺得可以更容易地掌握TCP?;蛘?br>根本談不上掌握,對于這種純理論的東西,即使你現在掌握了再多的細節,一段時間后也會淡忘。
在以后各種細節中,因為我們會涉及到分析一些TCP中的數據報,因此一個協議包截獲工具必不可少。在
<TCP/IP詳解>中一直使用tcpdump。這里因為我的系統是windows,所以只好使用windows平臺的tcpdump,
也就是WinDump。在使用WinDump之前,你需要安裝該程序使用的庫WinpCap。
關于WinDump的具體用法你可以從網上其他地方獲取,這里我只稍微提一下。要讓WinDump開始監聽數據,
首先需要確定讓其監聽哪一個網絡設備(或者說是網絡接口)。你可以:

獲取當前機器上的網絡接口。然后使用:

開始對網絡接口2的數據監聽。windump如同tcpdump(其實就是tcpdump)一樣支持過濾表達式,windump
將會根據你提供的過濾表達式過濾不需要的網絡數據包,例如:

那么windump只會顯示端口號為4000的網絡數據。
序號和確認號:
要講解TCP的建立過程,也就是那個所謂的三次握手,就會涉及到序號和確認號這兩個東西。翻書到TCP
的報文頭,有兩個很重要的域(都是32位)就是序號域和確認號域??赡苡行┩瑢W會對TCP那個報文頭有所
疑惑(能看懂我在講什么的會產生這樣的疑惑么?),這里我可以告訴你,你可以假想TCP的報文頭就是個
C語言結構體(假想而已,去翻翻bsd對TCP的實現,肯定沒這么簡單),那么大致上,所謂的TCP報文頭就是:






















那么,這個序號和確認號是什么?TCP報文為每一個字節都設置一個序號,覺得很奇怪?這里并不是為每一
字節附加一個序號(那會是多么可笑的編程手法?),而是為一個TCP報文附加一個序號,這個序號表示報文
中數據的第一個字節的序號,而其他數據則是根據離第一個數據的偏移來決定序號的,例如,現在有數據:
abcd。如果這段數據的序號為1200,那么a的序號就是1200,b的序號就是1201。而TCP發送的下一個數據包
的序號就會是上一個數據包最后一個字節的序號加一。例如efghi是abcd的下一個數據包,那么它的序號就
是1204。通過這種看似簡單的方法,TCP就實現了為每一個字節設置序號的功能(終于明白為什么書上要告訴
我們‘為每一個字節設置一個序號’了吧?)。注意,設置序號是一種可以讓TCP成為’可靠協議‘的手段。
TCP中各種亂七八糟的東西都是有目的的,大部分目的還是為了’可靠‘兩個字。別把TCP看高深了,如果
讓你來設計一個網絡協議,目的需要告訴你是’可靠的‘,你就會明白為什么會產生那些亂七八糟的東西了。
接著看,確認號是什么?因為TCP會對接收到的數據包進行確認,發送確認數據包時,就會設置這個確認號,
確認號通常表示接收方希望接收到的下一段報文的序號。例如某一次接收方收到序號為1200的4字節數舉報,
那么它發送確認報文給發送方時,就會設置確認號為1204。
大部分書上在講確認號和序號時,都會說確認號是序號加一。這其實有點誤解人,所以我才在這里廢話了
半天(高手寬容下:D)。
開始三次握手:
如果你還不會簡單的tcp socket編程,我建議你先去學學,這就好比你不會C++基本語法,就別去研究vtable
之類。
三次握手開始于客戶端試圖連接服務器端。當你調用諸如connect的函數時,正常情況下就會開始三次握手。
隨便在網上找張三次握手的圖:
如前文所述,三次握手也就是產生了三個數據包??蛻舳酥鲃舆B接,發送SYN被設置了的報文(注意序號和
確認號,因為這里不包含用戶數據,所以序號和確認號就是加一減一的關系)。服務器端收到該報文時,正
常情況下就發送SYN和ACK被設置了的報文作為確認,以及告訴客戶端:我想打開我這邊的連接(雙工)??蛻?br>端于是再對服務器端的SYN進行確認,于是再發送ACK報文。然后連接建立完畢。對于阻塞式socket而言,你
的connect可能就返回成功給你。
在進行了鋪天蓋地的羅利巴索的基礎概念的講解后,看看這個連接建立的過程,是不是簡單得幾近無聊?
我們來實際點,寫個最簡單的客戶端代碼:









主要就是connect。運行程序前我們運行windump:






如何分析windump的結果,建議參看<tcp/ip詳解>中對于tcpdump的描述。
建立連接的附加信息:
雖然SYN、ACK之類的報文沒有用戶數據,但是TCP還是附加了其他信息。最為重要的就是附加的MSS值。這個
可以被協商的MSS值基本上就只在建立連接時協商。如以上數據表示,MSS為1460字節。
連接的意外:
連接的意外我大致分為兩種情況(也許還有更多情況):目的主機不可達、目的主機并沒有在指定端口監聽。
當目的主機不可達時,也就是說,SYN報文段根本無法到達對方(如果你的機器根本沒插網線,你就不可達),
那么TCP收不到任何回復報文。這個時候,你會看到TCP中的定時器機制出現了。TCP對發出的SYN報文進行
計時,當在指定時間內沒有得到回復報文時,TCP就會重傳剛才的SYN報文。通常,各種不同的TCP實現對于
這個超時值都不同,但是據我觀察,重傳次數基本上都是3次。例如,我連接一個不可達的主機:



發出了三個序號一樣的SYN報文,但是沒有得到一個回復報文(廢話)。每一個SYN報文之間的間隔時間都是
有規律的,在windows上是3秒6秒9秒12秒。上面的數據你看不到12秒這個數據,因為這是第三個報文發出的
時間和connect返回錯誤信息時的時間之差。另一方面,如果連接同一個網絡,這個間隔時間又不同。例如
直接連局域網,間隔時間就差不多為500ms。
(我強烈建議你能運行windump去試驗這里提到的每一個現象,如果你在ubuntu下使用tcpdump,記住sudo :D)
出現意外的第二種情況是如果主機數據包可達,但是試圖連接的端口根本沒有監聽,那么發送SYN報文的這
方會收到RST被設置的報文(connect也會返回相應的信息給你),例如:






可以看出,7AURORA-CCTEST.7100返回了RST報文給我,但是我這邊根本不在乎這個報文,繼續發送SYN報文。
三次過后connect就返回了。(數據反映的事實是這樣)
關于listen:
TCP服務器端會維護一個新連接的隊列。當新連接上的客戶端三次握手完成時,就會將其放入這個隊列。這個隊
列的大小是通過listen設置的。當這個隊列滿時,如果有新的客戶端試圖連接(發送SYN),服務器端丟棄報文,
同時不做任何回復。
總結:
TCP連接的建立的相關要點就是這些(or more?)。正常情況下就是三次握手,非正常情況下就是SYN三次超時,
以及收到RST報文卻被忽略。
posted on 2008-05-11 01:03 Kevin Lynx 閱讀(3695) 評論(10) 編輯 收藏 引用 所屬分類: game develop 、network