近期工作,跟網(wǎng)絡(luò )協(xié)議相關(guān),這讓我有機會(huì )更深入學(xué)習網(wǎng)絡(luò )協(xié)議,而之前很長(cháng)一段時(shí)間,我對網(wǎng)絡(luò )協(xié)議的理解都停留在比較淺的層面。
比如:TCP是面向連接的、可靠傳輸,而UDP是非連接的、不可靠傳輸,TCP建連需要3次握手,會(huì )造成delay,UDP更快。
比如:socket編程,服務(wù)器socket create、bind、listen、accept、read/write、shutdown/close,客戶(hù)端socket create、connect、read/write、shutdown/close,再加上epoll/select這幾下子。
再比如:我知道網(wǎng)絡(luò )編程要忽視SIGPIPE信號不然會(huì )掛,read返回0代表對端主動(dòng)關(guān)閉,非阻塞的read要放在循環(huán)里要考慮返回值,多路復用以及阻塞、非阻塞的區別。
TCP/UDP的區別上,我是這樣理解的:從北京到杭州,TCP相當于修了一條高鐵線(xiàn)路(建連)再通車(chē)發(fā)貨(傳輸數據),而UDP相當于寄快遞,丟了不管(直接傳輸數據)。
上面的理解對不對?可以說(shuō)對,也可以說(shuō)不對。對于應用程序員來(lái)說(shuō),有了上面的認識+熟悉socket編程接口,夠了嗎?不夠嗎?
大物理學(xué)家費曼提出一個(gè)高效的費曼學(xué)習法,即從問(wèn)題入手,試著(zhù)把問(wèn)題都講出來(lái),以教代學(xué),一旦你能把問(wèn)題都講清楚,便學(xué)會(huì )了。所以我想嘗試一下把TCP/IP講清楚,借此讓自己學(xué)明白,順便幫助一下讀者。
雖然《TCP/IP詳解卷1》是一本關(guān)于互聯(lián)網(wǎng)協(xié)議族很嚴謹詳盡的書(shū),但在我看來(lái),它稍微有點(diǎn)晦澀,可能需要讀幾遍,才能心領(lǐng)神會(huì )。雖然我沒(méi)有能力把這個(gè)問(wèn)題說(shuō)的更好,但因為我經(jīng)歷過(guò)從稀里糊涂到稍有所悟的過(guò)程,這可能是大師不可比的,我將盡量用通俗易懂的語(yǔ)言把TCP/IP相關(guān)的知識講清楚。
TCP/IP協(xié)議族是一組協(xié)議的集合,也叫互聯(lián)網(wǎng)協(xié)議族,用來(lái)實(shí)現互聯(lián)網(wǎng)上主機之間的相互通信。TCP和IP只是其中的2個(gè)協(xié)議,也是很重要的2個(gè)協(xié)議,所以用TCP/IP來(lái)命名這個(gè)互聯(lián)網(wǎng)協(xié)議族,實(shí)際上,它還包括其他協(xié)議,比如UDP、ICMP、IGMP、ARP/RARP等。
大學(xué)《計算機網(wǎng)絡(luò )》教科書(shū)上有經(jīng)典的網(wǎng)絡(luò )ISO七層模型,但七層劃分太細了,稍顯繁瑣,不容易記住。
互聯(lián)網(wǎng)協(xié)議族TCP/IP按粗粒度的四層劃分,兩種劃分的對照圖讓彼此關(guān)系一目了然。
分層是計算機領(lǐng)域的常用技巧,比如互聯(lián)網(wǎng)后端的三層架構“接入-邏輯-存儲”就是分層思想的典型應用。
分層是為了隔離,通過(guò)分層劃分職能,拆解問(wèn)題,層與層之間約定接口,屏蔽實(shí)現細節。
TCP/IP自下到上劃分為鏈路層、網(wǎng)絡(luò )層、傳輸層、應用層。下層向上層提供能力,上層利用下層的能力提供更高的抽象。
1. 鏈路層,也稱(chēng)網(wǎng)絡(luò )接口層,包括操作系統的設備驅動(dòng)程序和網(wǎng)卡,它們一起處理與傳輸媒介(光纖等)的物理接口細節。
2. 網(wǎng)絡(luò )層,也就是IP層,負責處理IP datagram在網(wǎng)絡(luò )中的傳輸,IP層傳輸的是IP datagram,借助路由表,把IP datagram從網(wǎng)絡(luò )的一端傳輸到另一端,簡(jiǎn)而言之:IP實(shí)現包的路由傳輸,IP協(xié)議和路由器工作在網(wǎng)絡(luò )層。
3. 傳輸層,提供端到端之間的通信,包括提供面向連接和高可靠性的TCP,以及無(wú)連接不可靠的UDP。貌似TCP更好,但實(shí)際不是這樣,UDP因為不需要建連開(kāi)銷(xiāo),所以更快,應用得也很廣,比如新一代互聯(lián)網(wǎng)協(xié)議HTTP3就從TCP轉向UDP,應根據適應場(chǎng)景選擇傳輸層協(xié)議。
4. 應用層,跟應用相關(guān),不同應用解決不同問(wèn)題,需要不同的應用層協(xié)議。
鏈路層處理數據在媒介上的傳輸,以及主機與網(wǎng)卡、光纖等打交道的細節。因為與硬件相關(guān),所以需要借助系統的驅動(dòng)程序,鏈路層協(xié)議就是定義這些細節的,比如怎么把數據從網(wǎng)卡發(fā)送到光纖,采用什么格式編碼等,它解決的數據在媒介上表示、流動(dòng)的問(wèn)題。
光有鏈路層功能肯定是不夠的,網(wǎng)絡(luò )上有成千上萬(wàn)的機器,主機A與B通信,你不能將數據發(fā)到主機C,所以仿照現實(shí),要為主機分配網(wǎng)絡(luò )地址,通過(guò)IP地址去標識網(wǎng)絡(luò )中的一臺主機,發(fā)送一個(gè)數據包,需要正確路由到目的地,這就好比你從家到公司,要經(jīng)過(guò)哪些路徑,需要地圖,而路由表就類(lèi)似這張地圖。IP解決的是數據包在網(wǎng)絡(luò )中的傳輸路由的問(wèn)題。
有了網(wǎng)絡(luò )層的傳輸路由能力,還不夠,因為IP報在傳輸過(guò)程中可能丟包,比如中間經(jīng)歷過(guò)的路由器緩沖區滿(mǎn)了便會(huì )丟包,這樣不可靠,如果需要可靠傳輸的能力,便需要傳輸層基于IP層,提供更多的能力,TCP解決了可靠性問(wèn)題。具體而言,如果丟包了,TCP層會(huì )負責超時(shí)重傳,它通過(guò)接收確認和重傳機制保證了可靠傳輸。另外,因為IP報都是獨立路由的,所以從主機A到主機B,一份數據被拆分成x、y兩個(gè)IP報先后發(fā)送,這2個(gè)包可能選擇不同的傳輸路徑,這樣有可能y包先于x包到達,但我們希望在接收端(主機B)恢復這個(gè)數據的信息,但我們無(wú)法控制IP報的到達順序,所以,我們需要在接收端恢復數據,我只需要在x、y包里記錄它屬于數據塊的哪個(gè)部分,然后重組這份數據,這正是TCP做的,它會(huì )重新組裝IP報,從而保證順序性,遞交給應用層。
有時(shí)候并不需要保證可靠性和順序性,這便是UDP能提供的,它只是簡(jiǎn)單的把數據封裝成IP報,然后通過(guò)IP層路由發(fā)送到目的端。
再往上,便是應用層協(xié)議了,比如http,又比如游戲服務(wù)器自定義協(xié)議,應用層協(xié)議通;赥CP或者UDP做傳輸。
什么是協(xié)議?懶得去翻協(xié)議的各種權威定義了,我認為協(xié)議就是約定,跟現實(shí)生活中協(xié)議這個(gè)詞含義差不多。網(wǎng)絡(luò )協(xié)議就是通信雙方共同遵守的約定,更具體一點(diǎn),就是定義數據在網(wǎng)絡(luò )上傳輸的格式、規則和流程。
因為網(wǎng)絡(luò )是分層模型,不同層有不同層的作用,所以為各層定義各層的規則,各層對應的各層協(xié)議。
前面講了TCP/IP協(xié)議族包含很多協(xié)議,這些協(xié)議分屬不同的分層,承擔不同的作用。
應用層和傳輸層使用端到端(end-to-end)協(xié)議,網(wǎng)絡(luò )層提供的是逐跳(hop-by-hop)協(xié)議。
A給B通過(guò)網(wǎng)絡(luò )傳送一塊數據,可以設想僅僅是傳輸這塊原始數據是不夠的,因為網(wǎng)絡(luò )傳輸過(guò)程中,網(wǎng)絡(luò )包到了某個(gè)路由器,需要轉發(fā),而轉發(fā)必須依賴(lài)數據包的一些附加信息,比如目標機器。
發(fā)送端在發(fā)送數據的時(shí)候,將原始數據按照協(xié)議格式加上一些控制信息,包裝成可在網(wǎng)絡(luò )上正確傳輸數據包的過(guò)程叫封裝。
TCP/IP協(xié)議族是層層封裝的,從應用層到鏈路層,每經(jīng)過(guò)一層都要添加一些額外信息(首、尾部)。
更準確的說(shuō),在IP和鏈路層傳輸的數據單元叫分組(Packet),分組既可以是一個(gè)IP datagram也可以是IP datagram的一個(gè)分片(fragment)。
UDP的封裝跟TCP略有不同,主要體現在經(jīng)過(guò)傳輸層(UDP)之后添加的是8字節UDP首部,產(chǎn)生UDP datagram。
封裝過(guò)程中,經(jīng)過(guò)TCP/UDP層的時(shí)候,會(huì )把端口號添加到TCP/UDP首部;經(jīng)過(guò)IP層的時(shí)候,會(huì )把協(xié)議類(lèi)型(TCP or UDP or ICMP or IGMP)添加到IP首部;經(jīng)過(guò)鏈路層的時(shí)候,會(huì )把幀類(lèi)型(IP or ARP or RARP)添加到以太網(wǎng)首部。這些信息將被用于接收端的處理。
接收端收到數據后,要執行跟發(fā)送端相反的解封操作,我們可以把發(fā)送端的數據封裝比喻成洗澡后一層層穿衣服,而接收端的操作,類(lèi)似洗澡前一層層脫衣服,把首尾部剝離,獲取傳遞的原始數據。
因為網(wǎng)絡(luò )上的主機有不同字節序,現在要通過(guò)網(wǎng)絡(luò )傳輸,便需要約定統一的網(wǎng)絡(luò )字節序(大端序),采用小端序的主機在網(wǎng)絡(luò )傳輸數據的時(shí)候要轉為大端序。
互聯(lián)網(wǎng)上每個(gè)接口都有一個(gè)唯一的網(wǎng)絡(luò )地址,也叫IP地址,IP地址有IPv4和IPv6兩個(gè)版本,IPv4是32位4字節的整數,每個(gè)字節(8bit)的取值范圍是0~255,所以可以把4字節的IPv4用四個(gè)點(diǎn)分隔的byte值表示,比如140.252.13.88,每個(gè)十進(jìn)制數值對應32位整數中的每個(gè)字節,這種表示法叫點(diǎn)分十進(jìn)制表示法,很顯然,點(diǎn)分十進(jìn)制法和int32兩種表示法之間很容易相互轉換。
IPv4地址劃分為ABCDE五類(lèi),32位地址表示的數值空間有限,難以為互聯(lián)網(wǎng)上的所有聯(lián)網(wǎng)設備分配獨立的IP地址,所以便存在動(dòng)態(tài)分配、共享、公網(wǎng)+內網(wǎng)地址轉化(NAT)等問(wèn)題,本質(zhì)上是為了解決IP地址不夠用的問(wèn)題。
IPv6使用128bit,2的128次方就非常大了,號稱(chēng)可以為地球上每粒沙子分配一個(gè)ip地址。
IP數據報(網(wǎng)絡(luò )層)用IP地址、而以太網(wǎng)幀(鏈路層)則是用硬件(48位Mac)地址,ARP和RARP用于IP地址和硬件地址之間做映射(轉換)。
TCP/UDP采用16位端口號來(lái)識別(區分)應用,比如主機A向主機B發(fā)送了一個(gè)IP報,主機B的內核收到該IP報之后,應該交給哪個(gè)應用程序去處理呢?端口號就是用來(lái)干這個(gè)的,內核會(huì )維護端口號到應用程序之間的對應關(guān)系。
比較常用的應用層協(xié)議有約定的端口號,也就是知名端口號,而1024~5000之間的端口號是分配給TCP/IP臨時(shí)用的,而大于5000的另做他用。也就是說(shuō),你用TCP方式去連網(wǎng)絡(luò )服務(wù)器,本地為該socket分配的端口號會(huì )在1024~5000之間,這取決于操作系統的端口分配策略。
域名系統(DNS)提供主機名字和IP地址之間的轉換,比如www.baidu.com是一個(gè)域名,應用程序可以通過(guò)一個(gè)標準庫函數(gethostbyname)來(lái)獲得給定名字主機的IP地址,標準庫函數(gethostbyaddr)實(shí)現逆操作。
ip地址是一串數字,含義不清、也不便于記憶,主機名含義更清晰,www.baidu.com你就很容易記住,這也是為什么存在IP地址還需要主機名的原因。
接收端接收到以太網(wǎng)數據幀(Frame)之后,需要像剝洋蔥一樣,從協(xié)議棧由底向上升,即遵照鏈路層->網(wǎng)絡(luò )層->傳輸層->應用層的順序,去掉各層協(xié)議添加的首尾部,將數據取出,交給最上層應用程序,這個(gè)過(guò)程叫Demultiplexing,尊從書(shū)本的翻譯叫分用。
回顧前面封裝的描述,在傳輸層、網(wǎng)絡(luò )層、鏈路層,分別將端口號存入TCP/IP首部,將協(xié)議類(lèi)型存入IP首部,將幀類(lèi)型存入以太網(wǎng)幀首部。所以在接收端,將一層層拆掉首部,取出對應信息,然后做分派,丟給不同模塊處理,上圖就是整個(gè)處理過(guò)程。
本文講了地址、域名、端口、TCP/IP分層模型、封裝、分用等概念。
你最好能記住TCP/IP鏈路層->網(wǎng)絡(luò )層->傳輸層->應用層的四層劃分。
TCP segment、UDP datagram、IP datagram、IP fragment、以太網(wǎng)frame、以及IP層和鏈路層之間傳輸的數據單元packet,這些概念你最好分清楚,這樣交談的時(shí)候會(huì )顯得比較專(zhuān)業(yè)而不是很土。
數據封裝,多看幾遍你便能記住了。
TCP封裝格式:以太網(wǎng)首部(14)+IP首部(20)+TCP首部(20)+應用數據+以太網(wǎng)尾部(4)
UDP封裝格式:以太網(wǎng)首部(14)+IP首部(20)+UDP首部(8)+應用數據+以太網(wǎng)尾部(4)
應用層協(xié)議在應用層實(shí)現,而傳輸層、網(wǎng)絡(luò )層、鏈路層都是在內核實(shí)現,所以想修改或者優(yōu)化底層協(xié)議很難,因為你幾乎動(dòng)不了內核,因為網(wǎng)絡(luò )上的大量設備OS你沒(méi)法一并改過(guò)來(lái),這就是所謂的網(wǎng)絡(luò )設備僵化問(wèn)題,HTTP3用UDP替代TCP,就是想在應用層自己去實(shí)現可靠傳輸等。
每個(gè)以太網(wǎng)幀有長(cháng)度限制(48~1500),網(wǎng)絡(luò )上每個(gè)設備也有對包的長(cháng)度限制,IP報大了就要分片,分片可能發(fā)生在發(fā)送端,也有可能發(fā)生在中間設備,但應該盡量避免分片,IP報會(huì )帶有信息讓分片后可以重組,MTU的概念可以了解一下。
ICMP和IGMP邏輯上屬于網(wǎng)絡(luò )層,因為他們是IP協(xié)議的附屬協(xié)議,但實(shí)際上,ICMP和IGMP報文都被封裝為IP datagram傳輸,所以又可以把他們視為IP層之上的協(xié)議。
同樣ARP和RARP用于IP地址和硬件MAC地址相互轉換,邏輯上屬于鏈路層,但實(shí)際上arp和rarp報文跟IP datagram一樣,都被封裝成以太網(wǎng)Frame傳輸。
接收端收到以太網(wǎng)幀之后,會(huì )走分用流程,最終將原始數據交給應用程序。
TCP/IP協(xié)議的應用程序經(jīng)常使用socket編程接口。
有很多跟網(wǎng)絡(luò )相關(guān)的工具,比如ping、ifconfig、netstat、arp、tcpdump、wireshark等。
一年前,我對網(wǎng)絡(luò )編程這塊,腦子里充滿(mǎn)疑問(wèn)。
眾所周知,TCP建連三次握手和斷連四次握手,但如前所述,任何時(shí)候,從主機A都可以任意發(fā)一個(gè)IP報到主機B,網(wǎng)絡(luò )主機之間是通過(guò)IP層實(shí)現路由轉發(fā)的,兩點(diǎn)之間的每個(gè)IP報都是獨立路由的,既然這樣,為什么還要建連?還要浪費時(shí)間做A->B、B->A、A->B來(lái)回?直接把包發(fā)過(guò)去不就完了嗎?
假設通過(guò)AB建立的3個(gè)IP報的作用是表示AB之間的網(wǎng)絡(luò )連通性?哪又有什么作用?因為網(wǎng)絡(luò )是隨時(shí)變化的,此刻連通又不代表下一刻連通。建連之后似乎并不存在A(yíng)B之間的真正連接,只是兩端OS層面維護的一個(gè)狀態(tài)(數據對象)?是虛擬連接?
建連到底是什么意思?客戶(hù)端發(fā)送一個(gè)IP報到服務(wù)器去發(fā)起連接?那跟傳輸數據的普通IP報又有什么區別?
雙工是什么意思?為什么socket關(guān)閉一半傳輸之后就不能發(fā)送數據了?網(wǎng)絡(luò )上IP報不是可以任意傳輸嗎?這個(gè)限制是哪個(gè)地方添加的?
擁塞控制是什么?Nagle是什么?滑動(dòng)窗口是什么?TCP為什么要;?
socekt的編程接口和各種概念跟TCP/IP原理有怎樣的對應關(guān)系?學(xué)完TCP/IP原理對理解socket編程有什么幫助?
沒(méi)有深究TCP/IP原理之前,我其實(shí)是有很多問(wèn)題的,只是做應用程序開(kāi)發(fā),好像沒(méi)搞懂那些問(wèn)題也還可以湊合干,但終究是有點(diǎn)糊里糊涂,感覺(jué)不太爽。
本來(lái)我想一篇文章講清楚TCP/IP的主要內容,但是寫(xiě)著(zhù)寫(xiě)著(zhù)發(fā)現,這樣文章會(huì )非常長(cháng),所以我決定多寫(xiě)幾篇,每篇都講清楚一個(gè)主題。