4-4:TCP协议之TCP头部格式详解
文章目錄
- 一:TCP頭部格式詳解
- (1)4位首部長度
- (2)序列號和確認應答號
- A:可靠性問題
- B:32位序號和確認號
- (3)窗口大小
- (4)標志位
- (5)緊急指針
- A:帶外數據(out_of _band)
- B:URG
- (6)校驗和
- 二:Linux中的TCP實現
一:TCP頭部格式詳解
(1)4位首部長度
標準TCP報頭寬度為4個字節,總計4×5=20個字節,所以在收到一個TCP報頭后首先會讀取20個字節(也就是報頭信息)
報頭中的選項是可有也可以沒有的,關鍵就取決于4位首部長度。它表示該TCP頭部有多少個32位bit(也就是有多少個4字節)。這4位是一個無符號數,取值范圍為0000~1111,也即是0到15,當為1111時表示該TCP報頭有15個4字節(60字節),而標準長度是20字節,所以選項最多是40字節
4位首部長度不可能是全0,因為TCP報頭長度至少是20字節,也就是至少是20/4=5,對應二進制序列為0101
于是TCP分離有效載荷和報頭時,拿到TCP報文后先直接讀取20字節,然后拿出4位首部長度進行分析,如果就是5那么剩下的就是數據,如果不是5,再讀取相應字節數即可
**TCP和UDP不一樣,UDP中有包長度來確定數據的長度,在TCP中是不需要包長度的,因為TCP并不需要按照數據塊交付,是面向字節流的,怎么讀,讀多少是應用層協議需要做的事情,而它只負責將其讀入緩沖區中。**報文的完整性依靠校驗和完成
(2)序列號和確認應答號
A:可靠性問題
可靠性涉及很多層面,比如數據有序,準確等等。但是這一切一切都要有一個前提,那么就是如何保證數據對方收到?
假設有兩個人A和B隔著很遠的距離喊話。A首先向B喊:**B你聽到了沒有?**如果此時A沒有聽到B的回應,那么對于A來說他一般會有兩種感覺:我說的話你沒有聽見或者你說的話我沒有聽見, 顯而易見對于A來說他更傾向于第一種感覺,所以A在沒有得到反饋時他一定會再次喊話
那什么時候A能確認B已經收到了自己的信息呢?當然是B的收到信息反饋給了A,也就是說這種反饋信息只是對A是有價值的,A通過B的反饋信息確認了我的信息已經傳達到了。
但是這里又產生了新的問題,對于A來說它得到了B的反饋確認了自己的信息已經遞達,但是對于B呢,B在發出反饋信息時,對于B他如何確認自己的反饋信息也傳達到了A那里了呢?,顯然A也發送反饋信息給B,如果B收到了那么B也就確認了。但是這里往返傳遞,總會有一條消息處于未決狀態,總有一條消息無法得到收到反饋,最后一條消息無法保證可靠性
所以互聯網通信中是不可能有絕對的可靠性的,只能保證相對的可靠性,也就是這種相對可靠性指的是,只要收到了對方的應答,就認為之前的數據對方已經收到
于是我們說:TCP基于的是確認應答機制
在TCP三次握手中,客戶端發送請求號,服務端給客戶端的反饋就是圖中的ACK。
如果ACK在傳輸途中丟了,如果服務端長時間沒有接受到客戶端回應時,就會觸發重傳機制,進行重傳。
這里有一個很大的誤區:我們不需要對ACK再ACK,因為一旦你對ACK進行ACK了,那么你就需要對ACK的ACK再進行ACK,這是一個無休無止的過程。確認應答并不是對ACK保證可靠性,而是通過ACK保證上一次發的消息時可靠的
B:32位序號和確認號
網絡傳輸畢竟是遠距離傳輸,所以會受到各種因素的影響,在傳輸過程中極有可能出現丟包的現象發生。而如果包都能達到,有可能會涉及到的亂序的問題,所以如果保證數據按序到達也是可靠性的一種體現
32位序號
假如有三段TCP報文,需要按照以下順序發送給服務端
- 你好
- 在嗎
- 去吃飯
如果TCP沒有保證的有序的機制,那么這三段報文到達服務端的順序可能會多種多樣,比如
- 去吃飯
- 你好
- 在嗎
TCP中的32位序號可以保證數據有序到達。如果數據由客戶端發送給服務端,那么32位序號就由客戶端填寫,它保證的就是客戶端的數據有序到達服務端(反之亦然)。客戶端在發送時會為發送的數據報文進行編號,比如
服務端收到之后即便是亂序的,它也可以根據編號進行排序
32位確認號
上面的三條數據:1“你好”,2“在嗎”,3“去吃飯”在客戶端發送出去后,服務端接受會給三個ACK,分別為ACK1,ACK2,ACK3。那么這里存在一個問題,客戶端如何確認現在到來的ACK是對我發出的哪一條數據的確認反饋呢?,如果其中有一個ACK丟了,那還不亂套了。
32位確認序號可以保證得到的ACK是相應報文的反饋。舉個例子,客戶端個發送的順序為1,2,3,如果客戶端此時收到的ACK中的確認序號是2,那么對于客戶端來說,它就認為它之前發送的1號已經收到了,現在應該從2開始發,如果受到的ACK確認序號是9,那么表示2也收到了。
所以如果發送順序為1,2,3,那么ACK順序則應該為2,3,4。但如果確認序號中2,3丟了,只收到了4,對于客戶端來說它就會認為3之前的數據已經全部收到。所以這就代表TCP可以少量丟包
32位序號和確認序號為什么要同時存在
需要注意的是TCP是全雙工的。客戶端給服務端發送時,服務端關心的就是序號,客戶端關心的確認序號。但同時服務端也會給客戶端發送數據,所以同時客戶端也會關心序號,而服務端也會關心確認序號
(3)窗口大小
窗口大小是指發送方接受緩沖區中剩余空間的容量,用于流量控制。
- 如下,接受和發送緩沖區
因為窗口大小的目的就是要讓對方明白我這個接收端現在的接受緩沖區容量的大小,讓發送端看實際情況發送。不要造成我都快滿了,對方還硬要發的局面(雖然有滿了之后有重傳機制,但是這種占用網絡資源的行為是一種設計問題)
(4)標志位
TCP功能非常強大,有建立連接的、有關閉連接的,還有一些正常數據報文等等。那么雙方是如何區分不同功能的TCP報文呢?正是通過其中的標志位,如果某個標志位被設置為1,表示該標志位生效。
SYN
當SYN為1時,表示希望建立連接,并在其【序列號】的字段1進行序列號初始值的設定
FIN
當該位為1時,表示今后不再會有數據發送,希望斷開連接。當通信結束希望斷開連接時,通信雙發的主機可以相互交換FIN位為1的TCP段
ACK
當該位位1時,【確認應答】字段變為有效,TCP規定除了最初建立連接時的SYN包之外該位必須設置為1
PSH
當該位設置為1時,表示發送端催促接收端盡管向應用層交付數據,及時清空緩沖區,以便后續數據到來
RST
當該位設置為1時,表示TCP連接中出現異常必須強制斷開連接。
URG
當該位設置為1時,表示TCP報文中發送的數據含有緊急數據。也就是16位緊急指針只有當URG為1時才有效
(5)緊急指針
A:帶外數據(out_of _band)
傳輸層協議使用帶外數據(out-of-band,OOB)來發送一些重要的數據,如果通信一方有重要的數據需要通知對方時,協議能夠將這些數據快速地發送到對方.為了發送這些數據,協議一般不使用與普通數據相同的通道,而是使用另外的通道
如何理解呢?你可以想象一個場景:銀行處理業務時人們需要排隊,這時進來了一個強盜,他搖搖晃晃,大搖大擺排隊直接走到了銀行職員前面,為什么他這么放肆呢?因為他手里有一把槍,最后他用搶抵著職員的腦袋逼迫她先為自己辦理業務。
此時這個強盜就可以看作一個帶外數據,而職員也一定會優先處理他,因為它明白此時情況很緊急。
B:URG
linux系統的套接字機制支持低層協議發送和接受帶外數據.但是TCP協議沒有真正意義上的帶外數據.為了發送重要協議,TCP提供了一種稱為緊急模式(urgentmode)的機制.TCP協議在數據段中設置URG位,表示進入緊急模式.接收方可以對緊急模式采取特殊的處理.很容易看出來,這種方式數據不容易被阻塞,可以通過在我們的服務器端程序里面捕捉SIGURG信號來及時接受數據或者使用帶OOB標志的recv函數來接受.
(6)校驗和
校驗和失敗,丟棄數據。觸發重傳
- 比如發送順序報文1,2,3,4,5,其中3號報文校驗和失敗,那么確認應答就是3,表示從3繼續開始發。
二:Linux中的TCP實現
Linux中TCP的報頭本質就是一個結構體
struct tcphdr {__be16 source;//源端口號__be16 dest;//目的端口號__be32 seq;//序號__be32 ack_seq;//確認序號 #if defined(__LITTLE_ENDIAN_BITFIELD)//標志位采用位段實現__u16 res1:4,doff:4,fin:1,syn:1,rst:1,psh:1,ack:1,urg:1,ece:1,cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD)__u16 doff:4,res1:4,cwr:1,ece:1,urg:1,ack:1,psh:1,rst:1,syn:1,fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __be16 window;//窗口大小__sum16 check;//校驗和__be16 urg_ptr;//緊急指針 };總結
以上是生活随笔為你收集整理的4-4:TCP协议之TCP头部格式详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (王道408考研数据结构)第五章树-第一
- 下一篇: 【小白的CFD之旅】02 江小白