我不是計算機科班出身。記得大學的時候旁聽計算機系的網路課,當時計算機系使用教材是"
計算機網路--自頂向下方法與internet特色"的影印版,這本教材與眾不同的乙個地方就是作者
james f.kurose和
keith w.ross採用了'自頂向下'的編排思路,先從應用層開始,最後講到物理層。而且這本書在語言上形象生動,通俗易懂。只怪我當初沒有一心一意聽講,到現在存在我的腦子中的基本概念居多,深刻理解甚少。以致於工作後遇到此類的問題,只能惡補。這不,在12月1日凌晨全國統一簡訊類服務接入**的調整工作中,我就遇到了此類問題,不得不再次抱起w.richard stevens的'
tcp詳解卷一'啃了啃,回顧一下
tcp協議那些事兒。
做應用層網路程式開發的,手頭上都有一把利器:抓包工具,更專業的名詞就是協議分析工具,常用的且功能強大的協議分析工具有:
tcpdump(windows平台上的叫
windump)、
ethereal等。工作中常常會遇到因應用層程式在協議字段傳送和接收解析上不一致而出現'糾紛'問題,這時我們一般採用的在tcp層用協議分析工具抓取該層原始資料報作為'對峙'的證據;還有的就是在客戶端與伺服器端鏈結問題上的一些現象也需要到tcp層去分析原因,這就需要對tcp層的基本工作原理有乙個清晰的認識。
首先我們要明確:tcp頭部中設定的一系列域都是為了能達到分割、重傳、查重、重組、流控、全雙工的協議功能而設定的,這裡比較重要的字段就是序列號和確認號。由於要達到重傳、查重、重組、全雙工這些目的,tcp層需要通過序列號和確認號來保證。序列號用來標識傳送端傳送資料報的順序,並且指導接收端對多資料報進行順序重組;傳送端傳送乙個資料報後,它會把這個資料報放入重發佇列中,同時啟動計時器,如果收到了關於這個包的確認資訊,便將此資料報從佇列中刪除;如果在計時器超時的時候仍然沒有收到確認資訊,則需要重新傳送該資料報。
client --> 置syn標誌 序列號 = j,確認號 = 0 ----> server
client <-- 置syn標誌 置ack標誌 序列號 = k, 確認號 = j + 1 <-- server
clinet --> 置ack標誌 序列號 = j + 1,確認號 = k + 1 --> server
鏈結建立後,接下來client端傳送的資料報將從j + 1開始,server端傳送的資料報將從k + 1開始,這裡要說明的是:建立鏈結時,client端宣稱自己的初始序列號是j,server端宣稱自己的初始序列號是k,但是建立連線後的資料報卻各自中初始序列號+1開始,這是因為syn請求本身需要占用乙個序列號。
在資料傳輸階段,按照常理應用層資料的傳輸是這樣的:(我們假定建立連線階段client端最後的確認包中序列號 = 55555, 確認號 = 22222)
client --> 置psh標誌,置ack標誌 序列號 = 55555, 確認號 = 22222,資料報長度 = 11 ---> server
client <-- 置ack標誌,序列號 = 22222, 確認號 = 55566 (=55555 + 11),資料報長度 = 0 <--- server
client <-- 置psh標誌,置ack標誌 序列號 = 22223, 確認號 = 55566,資料報長度 = 22 <--- server
client --> 置ack標誌,序列號 = 55566, 確認號 = 22244(=22222+22),資料報長度 = 0 ---> server
注:psh標誌指示接收端應盡快將資料提交給應用層。從我協議分析的經歷來看,在資料傳輸階段,幾乎所有資料報的傳送都置了psh位;而ack標誌位在資料傳輸階段也是一直是置位的。
但是實際我們在分析過程看到的卻都是如下這樣的:
client --> 置psh標誌,置ack標誌 序列號 = 55555, 確認號 = 22222,資料報長度 = 11 ---> server
client <-- 置psh標誌,置ack標誌 序列號 = 22222, 確認號 = 55566 (=55555 + 11),資料報長度 = 22 <--- server
client --> 置psh標誌,置ack標誌 序列號 = 55566, 確認號 = 22244 (=22222 + 22),資料報長度 = 33 ---> server
client <-- 置psh標誌,置ack標誌 序列號 = 22244, 確認號 = 55599 (=55566 + 33),資料報長度 = 44 <--- server
也就是說:資料接收端將上乙個應答和自己待傳送的應用層資料組合在一起傳送了。tcp的傳輸原則是儘量減少小分組傳輸的數量,所以一般預設都開啟"帶時延的ack"。一般實現中,時延在200ms。nagle演算法多用來實現"帶時延的ack",它要求乙個tcp連線上最多只能有乙個未被確認的小分組。在該分組的確認到達之前不能傳送其他小分組。也就是說:傳送端在傳送乙個分組後,需等待這個分組的ack確認後,才可以進行下乙個分組的傳送。這樣一來網路的傳輸效率被大大降低了。對於大資料塊的傳輸來說,這樣很多時候是難以忍受的。另一種擁塞控制策略被引入,那就是tcp的滑動視窗協議,滑動視窗協議是分組傳送和分組確認不再同步,傳送端可以連續傳送n個分組,接收端同樣也可以用乙個確認包來一起確認這n個分組,通常n = 2。各種os的tcp協議棧在實現上都是綜合了nagle演算法和滑動視窗協議的,tcp層對應用層資料分組大小進行
多次判斷(一般分組大小都是和mss做比較的),以在nagle和滑動視窗協議之間抉擇到底選擇哪一種控制方式進行傳送。"
the linux network architecture: design and implementation of network protocols in the linux kernel"一書介紹了linux在tcp層上的設計和實現,當然最直觀的還是去分析
linux源**了。
拆除tcp連線過程用一句話表述就是:你關你的傳送通道,我關我的傳送通道(因為tcp是全雙工)。當一方關閉傳送通道後,仍可接收另一方傳送過來資料,這樣的情況就成為"半關閉"。然而多數情況下,"半關閉"使用的很少,而且半關閉需要socket aip支援在socket上的shutdown(而不是呼叫close)。
正常的關閉流程是源於fin報文的:
client --> fin ack --> server
client <-- ack <-- server
client <-- fin ack -- server
client --> ack --> server
傳送fin分組的一端會先將傳送緩衝中的報文按序發完之後,再發出fin;所以說fin又叫做:orderly release。
異常的關閉流程是源於rst報文的。乙個典型的例子就是當客戶端所要鏈結的伺服器端的埠並沒有程式在listen,這時伺服器端的tcp層會直接傳送乙個rst報文,告訴客戶端重置連線。rst報文是無需確認的。客戶端在收到rst後會通知應用層對方異常結束鏈結(需通過socket api的設定才能得知對方是異常關閉)。
TCP協議理解高階
現在我們來介紹一下tcp協議的執行問題,因為我們對tcp協議實際上是什麼樣子知道的並不多。任何一方都可以關閉乙個tcp連線,要求雙方傳送乙個fin訊號關閉自己的通訊頻道。一方可以在另一方之前關閉,或者雙方同時關閉tcp連線。因此,當一方傳送乙個fin訊號時,另一方可傳送 fin ack 開始關閉自己...
TCP協議 HTTPS協議的理解
tcp tcp是一種可靠的傳輸協議,為什麼說它可靠呢,因為它有 三次握手 為什麼握三次手就可靠了,握兩次或者一次會怎樣?hi,我可以跟你拉小手手嗎?ok啊,那你現在方便跟我牽嗎?伸出手 好的,我已經抓住你的手了 https是加了安全驗證的http協議。可以這麼說,當你跟某個遠端的小夥伴通過網路傳輸資...
TCP協議深入理解
tcp協議在能夠傳送資料之前就建立起了 連線 要實現這個連線,啟動tcp連線的那一方首先將傳送乙個syn資料報。這只是乙個不包含資料的資料報,然後,開啟syn標記。如果另一方同時在它收到syn標記的埠通話,它將發回乙個syn ack syn和ack標誌位都被開啟,並將ack 確認 編號字段設定為剛收...