歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

UNIX網絡編程筆記(1)—傳輸層協議

開始學習網絡編程的經典《UNIX網絡編程》(第3版)作為研究生階段的副本練習吧,厚厚一本書,希望能堅持看下去,堅持做些筆記。


1.TCP/IP協議概述

IPv4

網際協議版本4(Internet Protocol version 4),32位地址,為TCP、UDP、SCTP、ICMP和IGMP提供分組遞送服務。

IPv6

網際協議版本6(Internet Protocol version 6)。128位地址,為TCP、UDP、SCTP和ICMPv6提供分組遞送服務。

TCP

傳輸控制協議(Transmission Control Protocol)。面向連接的傳輸層協議,提供可靠的全雙工字節流,使用流套接字(stream socket)。

UDP

用戶數據報協議(User Datagram Protocol)。UDP是一個無連接的傳輸層協議,使用數據報套接字(datgram socket)。

SCTP

流控制傳輸協議(Stream Control Transmission Protocol)。是可靠全雙工關聯的面向連接的傳輸層協議。SCTP是多宿的,每個關聯的兩端均涉及一組IP地址和一個端口號。這裡多宿的概念簡單理解就是多個網卡。

ICMP協議

網際控制消息協議(Internet Control Message Protocol)。ICMP處理在路由器和主機之間流通的錯誤和控制信息。這些消息由TCP/IP網絡支持軟件本身(而不是用戶進程)產生和處理,例如ping和traceroute使用ICMP。

IGMP協議

網際組管理協議(Internet Group Management Protocol)。IGMP用於多播。

ARP(Address Resolution Protocol)和RARP(Reverse Address Resolution Protocol)

ARP地址解析協議把一個IPV4映射成一個硬件地址(以太網地址)。RARP反向地址解析協議把一個硬件地址映射成一個IPv4地址。


2.用戶數據報協議(UDP)

UDP是無連接不可靠的傳輸層協議,著些主要體現在:
當用戶進程往一個UDP套接字寫入消息,消息隨後被封裝到一個UDP數據報(每個數據報都會有一個長度記錄被接收端應用程序獲取),進而又被封裝到一個IP數據報,然後發往目的地,但是:UDP不保證數據報會到達其最終目的地,不保證各個數據報到達的先後順序,也不保證數據報只到達一次


3.傳輸控制協議(TCP)

TCP向應用程序提供面向連接的可靠性服務

面向連接體現在:TCP客戶端要首先與某個服務器建立一個連接,然後再誇該連接交換數據,最後終止連接。

可靠性體現在:TCP不保證數據一定會被對方端點接收,它提供的是數據的可靠遞送(等待確認並自動重傳)和故障的可靠通知(放棄重傳或中斷連接)。在數次重傳失敗後,TCP才會放棄,如此在嘗試發送數據所花的時間一般為4~10分鐘。

TCP具有的一些能力

可以動態估算客戶和服務器之間的往返時間(round-trip time RTT),以便知道等待一個確認需要多少時間。例如RTT在局域網中幾毫秒,在廣域網則要數秒。

TCP對所發的數據進行排序:即在每個分節(TCP傳遞給IP的數據單元)的內部給每個字節關聯一個序號,這就可以保證:在應用接收數據之前對非順序到達的數據進行排序、並且對對端的重復數據進行丟棄。

TCP提供流量控制(flow control),流量控制確保發送端發送的數據不會使得接收端緩沖區溢出:當發送數據太快,而接收端來不及接收時,為了保證數據不丟失,必須協調好雙方通信的節奏,通告窗口就起到了這樣的作用:當接收來自發送端的數據時,窗口大小減小,當接收端從緩沖區讀走數據時,窗口就變大;當窗口為0時,說明TCP對應某個套接字的緩沖區已滿,此時它必須等待對端從緩沖區讀取數據和不為0的通告窗口消息的到來。

TCP連接是全雙工的(UDP也可以是全雙工),也就是說在一個給定的連接上,應用可以在任何時刻在近處的兩個方向上既發送數據又接收數據,需要的話也可以改成單工連接。


4.流控制傳輸協議(SCTP)

這個協議以前沒聽過,今天也學習一蛤。
SCTP支持多宿,與TCP相比提供兩個IP地址之間的通信,SCTP可以提供兩個系統之間的通信:一個端點可以有冗余的網絡連接,每個網絡又可以有各自接入因特網基礎設施的連接,這樣,乳溝某個網絡或者通路發生故障,SCTP可以切換到使用已與該關聯相關的另一個地址來規避故障。

SCTP是面向消息的。它提供各個記錄的按序遞送服務,與UDP記錄數據報長度一樣,SCTP也會寫入每條記錄的長度隨數據一道傳遞給接收端應用。
PS:UDP是面向數據報,TCP是面向字節流的。

與TCP不同的是,當TCP字節丟失,將阻塞其後數據的遞送,而SCTP則不會阻塞。


5.TCP的連接和終止

三路握手

我們知道TCP連接要經過三路握手,實際將發生下面這些情況:

服務器server端通過socket(創建套接字)、bind(綁定套接字)、listen(監聽套接字)這三個過程完成,被稱之為被動打開(passive open) 客戶端client通過調用connect發起主動打開(active open)去向一個固定IP和固定端口號的服務器發起連接。這導致客戶TCP發送一個SYN分節來告訴服務器將在接下來的發送數據時的初始序列號。SYN(Synchronous)是建立連接時使用的握手信號,通常SYN不攜帶數據只有一些報頭。 服務器要給客戶端回復一個ACK和一個SYN,ACK用來確認客戶的SYN,SYN包含服務器在該連接中發送的數據的初始序列號。 最後客戶必須確認第3點中服務器發送的SYN。
這裡寫圖片描述

這就是三路握手的過程,所謂的三路握手,指的是這種服務器與客戶端之間的交換至少需要3個分組。
根據圖示,為什麼客戶發送SYNJ時(客戶的初始序列號為J),服務器要回應$ACK_{J+1}(客戶端期待的下一個序列號)?原因很簡單,因為SYN所在字節本身也是一個序列號空間。<喎?http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsrp1tC4+LP20ru49sD919PAtMu1w/dUQ1C9qMGi0ru0zsGsvdOjujwvcD4NCjxibG9ja3F1b3RlPg0KCTxwPjxzdHJvbmc+t/7O8cb3tsujujwvc3Ryb25nPjxiciAvPg0KCXNvY2tldLS0vajM173T19Y9wvLSu7j2yta7+jxiciAvPg0KCWJpbmSw87aozNe909fWPbDs0rvVxb+osqKw0brFwuu45svfsfDIyzxiciAvPg0KCWxpc3RlbrzgzP3M173T19Y9sNHK1rv6v6rG9M6qz+zB5cSjyr08YnIgLz4NCglhY2NlcHS007XItP3BrL3TttPB0NbQs+nIodK7uPbBrL3To6yyorS0vajSu7j20MK1xMzXvdPX1re1u9i/zbuntcSx6sq2PcC0tefP1Mq+PGJyIC8+DQoJPHN0cm9uZz6/zbuntsujujwvc3Ryb25nPjxiciAvPg0KCWNvbm5lY3Q9sqa08ta4tqi6xcLrtcS157uwPC9wPg0KPC9ibG9ja3F1b3RlPg0KPGgzIGlkPQ=="tcp選項">TCP選項

每個SYN可以有一些TCP選項,可以在待建立的連接中起到配置作用:

MSS選項:MSS=Maximum Segment Size,即最大分節大小,發送端TCP使用接收端的MSS值作為所發送分節的最大大小。 窗口規模選項:TCP使用滑動窗口進行流量控制,最大窗口大小是65535,這是因為TCP首部中配置窗口大小的相應字段占16位。 時間戳選項:這個選項針對於高速網絡,以防止由失而復得的分組可能造成的數據損壞。

TCP連接終止

TCP建立一個連接需要3個分節,而終止一個連接需要4個分節,具體過程如下:

客戶端首先調用close關閉套接字(主動關閉),該端的TCP會發送一個FIN分節。 服務器收到FIN後首先會發送一個ACK確認,同時會傳遞給本端接收應用進程一個文件結束符(EOF),這個結束符放在接收到的數據的緩沖隊列的隊尾,由於FIN的收到,也不會再收到額外數據了。 一段時間後,接收到文件結束符的應用調用close關閉套接字,此時服務端TCP也會發送一個FIN。 主動發起連接的一端收到FIN後會發送一個ACK作為確認。

每個方向都需要有一個ACK和一個FIN,所以說通常是4個字節。如圖所示:

這裡寫圖片描述

 

上述舉例是客戶端主動發起關閉(通常情況下是這樣),實際上在某些協議(例如HTTP/1.0)可以由服務器執行主動關閉,事實上,在書中第1章給出的獲取時間的demo中,都是服務器主動斷開連接。

半關閉的概念:當服務器收到FIN之後,在它向客戶端回應一個ACK和發送一個FIN之間(上述2,3步驟)是可以有數據流的,使用shutdown函數。

TCP狀態轉換圖

TCP為一個連接定義了11種狀態。這裡就直接貼出電子書中截出的圖。

這裡寫圖片描述

圖中可以看出,客戶端和服務器都對應自己的狀態轉移圖,並且此處不考慮客戶端和服務器同時打開和同時關閉的情況,這種情況下,網絡中會出現SYN或FIN交錯的情況。可以使用netstat工具去監視狀態的變化情況。

 

觀察分組

書中給出了一個完整的TCP連接所發生的分組交換情況,這裡也貼出圖來:

這裡寫圖片描述

 

過程很簡單:客戶服務器建立三次握手連接->客戶與服務器進行數據交互->客戶主動關閉連接

這裡我比較在意的是兩個MSS值的定義,圖中箭頭所示,網上看了不少資料得出如下結論:

IP數據包包頭和TCP數據包包頭分別是20bytes和20bytes MTU(最大傳輸單元)由硬件特性決定,以太網為例,其MTU=1500bytes MTU=IP報頭+TCP報頭+MSS 如果IP數據報大於MTU則IP報文就要分片傳輸,而TCP報文如果超過MSS,則TCP進行分段傳輸。 在IP不分片的前提下計算的得到MSS=1500-20-20=1460bytes Internet上標准的MTU是576bytes(MSS=576-20-20=536)

根據上述結論,就理解了書中1460和536的設定了。另外,服務器對客戶請求的確認是伴隨其應答發送的,這種做法被稱作捎帶(piggybacking),它通常在服務器處理請求並產生應答的時間少於200ms發生。

單從書中例子給出的交換來說,除了數據請求和數據應答,其余至少8個分節都是TCP的額外開銷(除了建立連接和終止連接的7個分節外,還包括數據應答ACK)

SYN J
SYN K、 ACK J+1
ACK K+1
應答ACK
FIN M
ACK M+1
FIN N
ACK N+1


6.TIME_WAIT狀態

書中單獨拉出來作為一個小節出來,想必很重要吧。
首先,TIME_WAIT狀態的停留時長是最長分節生命期(Maximum Segment Lifetime,MSL)的兩倍,有時候稱之為2MSL。
TIME_WAIT狀態的存在有兩個理由:

可靠地實現TCP全雙工連接的終止 允許老的重復分節在網絡中消逝

第1個理由很好理解:假設服務器端發送FIN N,而客戶端的第一個ACK N+1因為某些原因丟失了,這個時候服務器久久沒有收到ACK就會超時重傳一個FIN N,那麼此時客戶端必須維護當前的狀態信息,這樣它才能重新發送那個最終的ACK N+1。
更廣義的說法:如果連接雙方的某一端發起了主動關閉連接,那麼它最終會進入TIME_WAIT狀態,因為如果出現異常最終重傳的ACK就是由主動發起關閉連接的那一端發送的。

第2個理由就有點繞口了,我們假設同一個IP和端口之間有一個TCP連接,我們關閉這個連接,過一段時間後在相同的IP地址和端口號之間建立另一個連接。後一個連接成為前一個連接的化身,因為他們的IP地址和端口號相同。TCP防止某個老的重復的分組,在該連接終止後再現,從而被誤解成屬於同一連接的某個化身。為了做到這一點,TCP將不給處於TIME_WAIT狀態的連接發起新的化身,(這句話我的理解是,如果在連接雙方,某一端進入了TIME_WAIT狀態,那麼,TCP將不會再在連接雙方所對應的IP和端口發起連接,即所謂的化身,此時這個即將斷開的連接中還有多的重復的分組)。既然TIME_WAIT狀態持續時間是MSL的兩倍,那麼這個時間足以讓某個方向上的分組最多存活MSL秒就被丟棄,另一個方向上的應答最多存活MSL也被丟棄。

總結這兩個理由可以歸結為兩句話:

**1. 萬一在最中確認連接中斷時服務器的ACK回應丟失怎麼辦,這樣就必須再次重傳FIN。
2. 萬一在極短的時間內從新有一個相同套接字連接建立怎麼辦,這樣就必須處理完上一次數據。**


7.SCTP簡介

SCTP也是面向連接的,因而也有關聯的建立和終止的握手過程。與tcp不同的是,建立連接需要4個分節(四路握手),關閉連接需要3個分節。SCTP的思路握手跟TCP的三路握手類似,差別在於作為SCTP整體一部分的cookie的生成,這個cookie包含了服務器IP地址清單、初始序列號等內容。SCTP重要程度次於TCP,這裡僅僅貼出書中示意圖。

四路握手示意

 

這裡寫圖片描述

 

關聯終止示意

 

這裡寫圖片描述

 

SCTP中的分組交換

 

這裡寫圖片描述

 


8.端口號

多個進程可能同時使用傳輸層協議,端口號(16位整數)用來區分這些進程。
眾所周知的端口號:0-1023
已登記端口號:1024-49151
動態或者私用端口:49152-65535
常用端口號:

協議 端口號 HTTP協議代理服務器常用端口號 80、8080、3128、8081、9080 FTP(文件傳輸)協議代理服務器常用端口號 21/TCP TFTP(Trivial File Transfer Protocol ),默認的端口號 69/UDP SMTP Simple Mail Transfer Protocol (E-mail),默認的端口號 25/TCP SSH(安全登錄)、SCP(文件傳輸)、端口重定向,默認的端口號 22/TCP

另外,客戶通常使用短期存貨的臨時端口號,這些端口號由傳輸層協議自動賦予客戶。

套接字與套接字對

套接字:標識每個端點的兩個值IP地址和端口號
套接字對:定義一個TCP連接的兩個端點的四元組{本地IP:本地端口號,外地IP:外地端口號}

並發服務器中主服務器循環派生子進程來處理新的連接。
TCP通過套接字對來區分連接,比如說:
在服務器端,父進程的監聽套接字:{:21,:*}
fork子進程1的已連接套接字:{本地ip:21,外地ip1:外地端口號1}
fork子進程2的已連接套接字:{本地ip:21,外地ip2:外地端口號2}


9.緩沖區大小限制

知道下面一些概念:
1. ipv4數據報最大大小是65535字節,包括ipv4的首部,這個原因是因為在IPV4報頭中用以表示其總長度的字段只有16位。ipv6是65575字節=65535+40,40表示ipv6首部。(ipv6中淨長度不包括首部)
2. MTU在上文中提到,由鏈路層的硬件規定,譬如說以太網的MTU是1500字節,IPv4要求的最小鏈路MTU是68字節。IPV6要求最小鏈路MTU是1280字節。
3. 關於分片的說法,當一個IP數據報從某個接口出去的時候,如果它的大小超過相應鏈路的MTU,則IPv4和IPv6將執行分片(fragmentation),在Windows下,可以通過ping命令來查看本機的MTU大小,例如:

ping -l 1800 -f www.baidu.com

-f選項給IPv4首部設置不分片(dont fragment)即DF位,而-l選項表示向目標主機發送多少字節的數據,雖然理論上來說最大可以是65535字節,然而在DOS下ping -l最大是65500字節(因為windows的漏洞所致),好了這些都不是重點。通過ping的方式,我們可以知道本機設置的MTU的大小(一般小於1500),當我們向目標主機發送大於MTU的數據並且強制設置不分片,系統就會提示錯誤:

需要拆分數據包但是設置DF

意思顯而易見就是說,你要發送這麼多數據超過了我MTU的大小現在必須要分片了,可是你卻又設置不分片。


10.TCP&UDP輸出

TCP

當某個應用進程寫數據到一個TCP套接字中時將經過下面步驟:

1.每個TCP套接字有一個發送緩沖區,我們可以使用SO_SNDBUF套接字選項來更改緩沖區大小。
2.當某個應用進程調用write時,內核從該應用進程的緩沖區復制所有數據到所寫套接字的發送緩沖區。
3.write函數是阻塞調用,也就是說,當套接字的發送緩沖區大小小於應用進程的數據時,應用進程將被投入休眠,直到應用進程緩沖區中的數據全部被寫入到套接字發送緩沖區,這也就意味著write函數調用成功返回,並不能說明對端已經接受到了數據。
4.每個發送端TCP套接字緩沖區需要保留數據副本直到被確認。
5.本端TCP以小於等於MSS大小的塊加上TCP首部構成TCP分節傳遞給IP。(這裡提到了一個最小重組緩沖區字節數,查了一下,這代表IPv4和IPv6都必須保證支持的最小數據報大小其中IPv4是576字節,IPv6是1500字節)
6.最後,IP層給每個TCP分節安上一個IP首部,並且根據TCP分節中提供的對端IP地址查找路由表項以確定外出接口,然後把數據報傳遞給相應額的數據鏈路。

UDP

當某個應用進程寫數據到一個UDP套接字中時將經過下面步驟:

1.UDP套接字沒有發送緩沖區,但是卻有一個SO_SNDBUF套接字選項來修改發送緩沖區的大小(這表示應用進程寫入該套接字的UDP數據報大小的上限。)
2.因為UDP是不可靠的,發送端不必保存數據的副本。
3.UDP數據報首部是8個字節,傳給IP層後,又安上相應的IP首部構成IP數據報,執行路由操作確定外出接口,或者直接把數據報加入數據鏈路層輸出隊列。
4.UDP沒有MSS因此比TCP更容易分片。
5.write返回成功表示縮寫數據報已經被加入數據鏈路層輸出隊列。

Copyright © Linux教程網 All Rights Reserved