TCP連接的建立和斷開、滑動窗口(四)

一、TCP段格式:



linux tcp結構體:

struct tcphdr {
	__u16	source;
	__u16	dest;
	__u32	seq;
	__u32	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	
	__u16	window;
	__u16	check;
	__u16	urg_ptr;
};

源端口號與目的端口號
源端口號和目的端口號,加上IP首部的源IP地址和目的IP地址唯一確定一個TCP連接。


序列號
序號表示在這個報文段中的第一個數據字節序號。(TCP把數據看成是有序的字節流,TCP隱式地對數據流的每個字節進行編號)


確認號
僅當ACK標誌爲1時有效。確認號表示期望收到的下一個字節的序號。


頭部長度(4)
4位,TCP頭部最多60個字節,最少20個字節。(5+選項個數)



保留位
6位,必須爲0


6個標誌位
URG-緊急指針有效
ACK-確認序號有效 (當ACK=0時,表示該數據段不包含確認信息,當ACK=1時,表示該報文段包括一個對已被成功接收報文段的確認。)
PSH-接收方應儘快將這個報文段交給應用層 (當PSH=1時,接收方在收到數據後立即將數據交給上層,而不是直到整個緩衝區滿。)
RST-連接重置 (用於重置一個已經混亂的連接(如主崩潰),也可用於拒絕一個無效的數據段或者拒絕一個連接請求。)
SYN-同步序號用來發起一個連接
FIN-表示將要終止一個連接 (表示發送方已經沒有數據要傳輸了)


窗口大小(用於流控制(確保連接的任何一方都不會過快地發送過量的分組而淹沒另一方),窗口大小指定了從被確認的字節算起可以發送多少個字節。)
通過窗口大小來達到流量控制。

校驗和
對tcp表頭與數據進行校驗。


緊急指針(URG)
是一個正的偏移量,與序號字段中的值相加表示緊急數據最後一個字節的序號。TCP的緊急方式是發送端向另一端發送緊急數據的一種方式。實際上,緊急數據跟帶外數據不是一回事,tcp並沒有另外建立一條邏輯連接傳輸數據,只是socket api 中把緊急數據叫做帶外數據而已。


選項與填充(選項爲4字節整數倍,否則用0填充)
最常見的可選字段是最長報文大小MSS(Maximum Segment Size),每個連接方通常都在通信的第一個報文段中指明這個選項。它指明本端所能接收的最大長度的報文段(payload)。該選項如果不設置,默認爲536(20+20+536=576B字節的IP數據報),其中ip首部和tcp首部各20個字節,而internet 上標準的MTU (最小)爲576B。


二、通訊時序(3次握手-->傳輸數據-->4次揮手)






在這個例子中,首先客戶端主動發起連接、發送請求,然後服務器端響應請求,然後客戶端主動關閉連接。兩條豎線表示通訊的兩端,從上到下表示時間的先後順序,注意,數據從一端傳到網絡的另一端也需要時間,所以圖中的箭頭都是斜的。雙方發送的段按時間順序編號爲1-10,各段中的主要信息在箭頭上標出,例如段2的箭頭上標着SYN, 8000(0), ACK 1001, <mss 1024>,表示該段中的SYN位置1,32位序號是8000,該段不攜帶有效載荷(數據字節數爲0),ACK位置1,32位確認序號是1001,帶有一個mss選項值爲1024。


建立連接的過程:
1. 客戶端發出段1,SYN位表示連接請求。序號是1000,這個序號在網絡通訊中用作臨時的地址,每發一個數據字節,這個序號要加1,這樣在接收端可以根據序號排出數據包的正確順序,也可以發現丟包的情況,另外,規定SYN位和FIN位也要佔一個序號,這次雖然沒發數據,但是由於發了SYN位,因此下次再發送應該用序號1001。mss表示最大段尺寸,如果一個段太大,封裝成幀後超過了鏈路層的最大幀長度,就必須在IP層分片,爲了避免這種情況,客戶端聲明自己的最大段尺寸,建議服務器端發來的段不要超過這個長度。
2. 服務器發出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示“我接收到序號1000及其以前所有的段,請你下次發送序號爲1001的段”,也就是應答了客戶端的連接請求,同時也給客戶端發出一個連接請求,同時聲明最大尺寸爲1024。
3. 客戶端發出段3,對服務器的連接請求進行應答,確認序號是8001。


在這個過程中,客戶端和服務器分別給對方發了連接請求,也應答了對方的連接請求,其中服務器的請求和應答在一個段中發出,因此一共有三個段用於建立連接,稱爲'''三方握手(three-way-handshake)'''。在建立連接的同時,雙方協商了一些信息,例如雙方發送序號的初始值、最大段尺寸等。


在TCP通訊中,如果一方收到另一方發來的段,讀出其中的目的端口號,發現本機並沒有任何進程使用這個端口,就會應答一個包含RST位的段給另一方。例如,服務器並沒有任何進程使用8080端口,我們卻用telnet客戶端去連接它,服務器收到客戶端發來的SYN段就會應答一個RST段,客戶端的telnet程序收到RST段後報告錯誤Connection refused.


數據傳輸的過程:
1. 客戶端發出段4,包含從序號1001開始的20個字節數據。
2. 服務器發出段5,確認序號爲1021,對序號爲1001-1020的數據表示確認收到,同時請求發送序號1021開始的數據,服務器在應答的同時也向客戶端發送從序號8001開始的10個字節數據,這稱爲piggyback。
3. 客戶端發出段6,對服務器發來的序號爲8001-8010的數據表示確認收到,請求發送序號8011開始的數據。

在數據傳輸過程中,ACK和確認序號是非常重要的,應用程序交給TCP協議發送的數據會暫存在TCP層的發送緩衝區中,發出數據包(TCP段)給對方之後,只有收到對方應答的ACK段才知道該數據包確實發到了對方,可以從發送緩衝區中釋放掉了,如果因爲網絡故障丟失了數據包或者丟失了對方發回的ACK段,經過等待超時後TCP協議自動將發送緩衝區中的數據包重發。


這個例子只描述了最簡單的一問一答的情景,實際的TCP數據傳輸過程可以收發很多數據段,雖然典型的情景是客戶端主動請求服務器被動應答,但也不是必須如此,事實上TCP協議爲應用層提供了全雙工(full-duplex)的服務,雙方都可以主動甚至同時給對方發送數據。


如果通訊過程只能採用一問一答的方式,收和發兩個方向不能同時傳輸,在同一時間只允許一個方向的數據傳輸,則稱爲'''半雙工(half-duplex)''',假設某種面向連接的協議是半雙工的,則只需要一套序號就夠了,不需要通訊雙方各自維護一套序號。


關閉連接的過程:
1. 客戶端發出段7,FIN位表示關閉連接的請求。
2. 服務器發出段8,應答客戶端的關閉連接請求。
3. 服務器發出段9,其中也包含FIN位,向客戶端發送關閉連接請求。
4. 客戶端發出段10,應答服務器的關閉連接請求。


建立連接的過程是三方握手,而關閉連接通常需要4個段,服務器的應答和關閉連接請求通常不合並在一個段中,因爲有連接半關閉的情況(調用shutdown而不是close),這種情況下客戶端關閉連接之後就不能再發送數據給服務器了,但是服務器還可以發送數據給客戶端,直到服務器也關閉連接爲止。


三、滑動窗口和流量控制

如果發送端發送的速度較快,接收端接收到數據後處理的速度較慢,而接收緩衝區的大小是固定的,就會丟失數據。TCP協議通過'''滑動窗口(SlidingWindow)'''機制解決這一問題。看下圖的通訊過程。




1. 發送端發起連接,聲明最大段尺寸是1460,初始序號是0,窗口大小是4K,表示“我的接收緩衝區還有4K字節空閒,你發的數據不要超過4K”。接收端應答連接請求,聲明最大段尺寸是1024,初始序號是8000,窗口大小是6K。發送端應答,三方握手結束。
2. 發送端發出段4-9,每個段帶1K的數據,發送端根據窗口大小知道接收端的緩衝區滿了,因此停止發送數據。
3. 接收端的應用程序提走2K數據,接收緩衝區又有了2K空閒,接收端發出段10,在應答已收到6K數據的同時聲明窗口大小爲2K。

4. 接收端的應用程序又提走2K數據,接收緩衝區有4K空閒,接收端發出段11,重新聲明窗口大小爲4K。
5. 發送端發出段12-13,每個段帶2K數據,段13同時還包含FIN位。
6. 接收端應答接收到的2K數據(6145-8192),再加上FIN位佔一個序號8193,因此應答序號是8194,接收端同時聲明窗口大小爲2K。
7. 接收端的應用程序提走2K數據,接收端重新聲明窗口大小爲4K。
8. 接收端的應用程序提走剩下的2K數據,接收緩衝區全空,接收端重新聲明窗口大小爲6K。
9. 接收端的應用程序在提走全部數據後,決定關閉連接,發出段17包含FIN位,發送端應答,連接完全關閉。


上圖在接收端用小方塊表示1K數據,實心的小方塊表示已接收到的數據,虛線框表示接收緩衝區,因此套在虛線框中的空心小方塊表示窗口大小,從圖中可以看出,隨着應用程序提走數據,虛線框是向右滑動的,因此稱爲滑動窗口


從這個例子還可以看出,發送端是一K一K地發送數據,而接收端的應用程序可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據,也就是說,應用程序所看到的數據是一個整體,或說是一個流(stream),一條消息有多少字節對應用程序是不可見的,因此TCP協議是面向流的協議,這也是容易出現粘包問題的原因。而UDP是面向消息的協議,每個UDP段都是一條消息,應用程序必須以消息爲單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不同的。


四、TCP如何保證可靠性

1、應用數據被分割成TCP認爲最適合發送的數據塊,稱爲段傳遞給IP層。
2、當TCP發出一個段後,它啓動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。
3、當TCP收到發自TCP連接另一端的數據段,它將發送一個確認。這個確認不是立即發送,通常將推遲幾分之一秒。
4、TCP將保持它首部和數據的校驗和。這是一個端到端的校驗和,目的是檢測數據在傳輸過程中的任何變化。如果收到段的校驗和有差錯,TCP將丟棄這個報文段並且不確認(導致對方超時重傳)
5、TCP承載於IP數據報來傳輸,而IP數據報的到達可能會失序,因此TCP報文段的到達也可能會失序。TCP將對收到的數據段進行重新排序後呈現在接收緩衝區給應用層提取
6、IP數據報會發生重複,TCP的接收端必須丟棄重複的數據。
7、TCP還能提供流量控制。TCP連接的每一方都有一定大小的緩衝空間。






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