文章首發於公眾號: 吳先生的伊甸園
本篇文章主要講的是計算機網路相關的內容,需要有一定的計算機網路的基礎知識才能汲取更多的知識。當然沒基礎也可以看懂,會對計算機網路有一個基礎的理解。在這一篇介紹中,我儘可能的覆蓋一些面試中的問題,通過本篇文章,無論你是實際開發,還是準備面試的過程中,都能對計算機網路都有一個比較深入的理解。
文章內容時不時會追根問底,比如三次握手的過程是什麼,深入一點的問題就是為什麼需要三次握手,而不是倆次握手,諸如此類的模式。文章可能會比較長,但是抽出半小時到一個小時的時間來讀此篇文章,會讓你理清計算機網路的相關知識。
本篇文章只會講到網路層之上,網路層之下的資料鏈路層和物理層本文不做深入的討論,畢竟在往下屬於便硬體相關的內容了,而不是軟體開發相關的內容。
文章的主要內容有:
計算機網路概述
應用層
HTTP
Socket
傳輸層
TCP
UDP
網路層
- IP
- DHCP
- ICMP
計算機網路概述:
計算機網路是通訊技術與計算機技術結合的產物,也就是說計算機網路就是為了解決計算機與計算機之間通訊的問題。什麼是通訊的問題,就是資料交換的問題,也是資訊交換的問題。在現實世界中,我們用資訊來形容交換的內容,在計算機世界裡,我們用「資料」這個詞來代替。這些詞都是對內容的抽象概括,因為現實世界需要交換的內容太複雜了,一段文字稱之為資訊,一張圖片也叫資訊。所以這些詞都是對這些內容的抽象概括,其實完全沒有那麼高大上,意會了就好。
繼續講,我們知道,與計算機網路相關的東西有哪些,大家可以列舉出很多,比如網線,網路卡,路由器,計算機中的 IP 地址。專業一點的人知道 TCP UDP,HTTP ,FTP。這些東西分別對應著計算機網路中不同的層次。層次有不同的分法, OSI(網路)模型將計算機網路分成了七層(搜尋關鍵詞 OSI 七層模型),因為分的太多太細,跟現實生活中操作有一些不匹配,被我們稱為理論上的成果,市場上的失敗,但卻是我們學習計算機網路的好工具。
在市場上成功的方式是將計算機網路分成五層或者四層。我喜歡按五層來分。剛剛列舉出關於計算機網路的內容中,網線屬於物理層,網路卡屬於資料鏈路層,路由器屬於網路層,對應的協議有 IP ,ICMP等。最後一個字母 P 代表 Protocol 協議的意思,因為狗血的語言不合的問題,我們還時不時的稱之為 IP 協議,HTTP 協議等,重在理解,叫什麼就無所謂了。
還有計算機網路擁有的是一個體繫結構,分成那麼多層是因為計算機網路體系太複雜了,還涉及到各種各樣的組成部分,一次性規範這麼多內容不太現實,所以我們按不同裝置按照功能劃分成不同的層次。這樣做的好處是每一層對其他層來說都是透明的,更利於標準化。某一層變化了,不影響其他層的工作。
分層思想在計算機領域應用的還是比較多的。分層帶來的好處就是透明,更容易制定標準。如何理解透明這一概念,透明的含義就是我不需要知道你是怎麼工作的,我想要什麼你能給什麼就行。最簡單的例子,我玩手機不需要知道手機是怎麼工作的,我只要會點螢幕就行,我能處理你給我展現的內容就行。螢幕顯示的內容就是手機提供給我們的內容。但是內部電池如何供電,供多大的電流,我們不需要考慮。這就是透明的含義。
在計算機網路中也是如此,不需要理解上層和下層是怎麼工作的,我只需要接受下層給我的資料,並且我能看懂,經過我這層之後,我按照上層在一開始規範好的資料格式,提交給上一層,上一層就會能正確的接收我提交的資料。分層之後,某一層的修改不會影響其他層。怎麼理解呢,IPv4 和 IPv6 都處於網路層,屬於不同版本的協議,但是從 IPv4 切換到 IPv6 對於應用層的 HTTP 來講是沒有區別的,HTTP 不需要管你用的是 IPv4 還是 IPv6,你按照我這 HTTP 的格式傳上來資料就行。就是這個意思。
應用層
應用層有著不同的協議,每個協議也都有不同的功能和用法,在這一層協議是最豐富的,但也更佳具體。比如 HTTP 用於我們網頁內容的傳輸。FTP 用於檔案的傳輸。SMTP 用於郵件的傳輸,還有 TELNET等等。每一個協議都比較複雜,都有詳細的規定,對協議的描述也比較細緻,實現的功能也比較多,所以就不在這裡寫了。如果你有需要,可以定向的瞭解某一個協議的詳細內容。
Socket
在應用層和傳輸層之間有一個介面叫做 Socket。Socket 不屬於某一個層,它是就像是應用層和傳輸層之間一個管道,用來連線作業系統和應用層中的具體的應用程式。應用可以操縱 Socket 來使用作業系統的網路功能。Socket 翻譯過來叫「套接字」。很奇怪的名字。因為傳輸層之下一般都是由作業系統來控制,而應用層的一些協議是由應用程式來控制的,所以我們需要一個介面來建立起應用程式和底層協議的橋樑,這個介面呢叫做 API(應用程式設計介面)。UNIX 定義了一個具體的實現,叫做 Socket。
現在大多數作業系統都支援 Socket。微軟也做了一個改進的API,叫做 winSock。其實就是 API 在不同作業系統上,定製化開發的一個 API,不知道你們理解了沒有。還要區分的是現在火起來的 WebSocket。WebSocket 是應用層的協議。而 Socket 不屬於某個層,實現的是程式跟作業系統在網路通訊方面的介面的具體實現。因為介面指的是 API,具體實現指的是 Socket。我們可以面向 Socket 程式設計,也就是自己定義了一個傳輸協議來實現計算機雙方的通訊。
就比如說你覺得 HTTP 太複雜了,狀態碼快取什麼的太臃腫了,我要自己寫一個程式,其實你只需要使用 Socket 傳送和接收資料,用程式碼來處理傳輸過來的資料,完全就完全能滿足計算機間的通訊問題,根本不需要在遵循其他應用層的協議了。你創造的資料處理規則就可以稱之為自定義的協議。
你寫的這個程式執行起來,作業系統就會自動分配給你一個埠號,在加上本機的 IP 地址,就能唯一的確定你這個程式的程式,等作業系統接收到發給你這個程式的資料,地址為 IP:分配的埠號 ,就會找到你的這個程式,把資料提交給你,程式就按照程式的邏輯來處理資料了。埠號是你使用 Socket 的一些函式時自動分配給你或者你手動配置的。那大家知道那麼多埠號是怎麼來的了吧,就是為了區分不同的程式,用於通訊的。像一些 Web server ,如 Nginx,肯定也利用了 Socket ,才能繫結了80埠。
傳輸層
傳輸層最典型的倆個協議是 TCP 和 UDP。當然還有其他的,如果好奇的可以去了解一下,比如 DCCP,SCTP 等。傳輸層提供的是應用程式之間的邏輯通訊機制。怎麼理解呢,我們知道電腦上執行著不同的程式,而在傳輸層,就要區分出這個資料是送到哪一個程式的。靠的是什麼呢,靠的就是埠號。一個網路程式一定會使用 Socket,使用 Socket 就會隨機分配一個埠號,或者利用 Socket 的繫結函式,手動把某一個埠號繫結到這個程式上。
多路複用,傳輸層有一個 TCP 和 UDP 多路複用的說法,這跟物理層中的多路複用不太一樣,物理層的多路複用指的是物理線路上的複用方式,包括分頻多工,分時多工,碼分複用,波長分波多工(與分頻多工屬於一個型別,波長與頻率是有關係的),而在傳輸層中講的多路複用是多個資料包同時接收,計算機是如何處理這些資料包的,是如何分發,按什麼原則分發,傳輸層的多路複用講的是這個。在傳輸層之下,所有資料包都經歷了相同的處理,但到了傳輸層不同的資料包傳輸就有了區別。「所有」其他主機給我傳送的「所有」UDP 資料包都會被提交給同一個埠。而根據連線的不同會將 TCP 資料包分給不同的埠。因為 TCP 連線是一對一的,每一個埠只對應一個連線,也同時對應另一臺主機上的唯一一個埠。就算另一臺主機想和我同時建立倆個TCP連線,那需要建立倆個程式或者執行緒,還需要倆個埠號。
下面詳細講一下 UDP 。
UDP 只是對 IP 協議進行簡單的擴充。很簡單的擴充。簡單到做了什麼事呢,就是網路層傳輸來的資料,區分一下埠號,就遞送給程式了,如果願意的話會選做一下錯誤檢測。除了這些,他什麼都沒有做。這種簡單的擴充帶來了很多好處,因為簡單,所以更容易的自定義,可以根據自己的需求新增一些功能,在哪自定義?在應用層。應用層可以處理 UDP 資料包,然後按照自己的想法去更改網路傳輸時的具體要求。UDP 除了易於在應用層對其擴充,還帶來的以下的好處:
- 相比於 TCP 不需要建立連線,所以傳送資料的延遲小,不用辛辛苦苦先建立好連線之後在傳送,可以想發就發。
- 實現比較簡單,不需要負責的實現過程。比如維護連線等。
- 頭部開銷比較小。在資料內容之上,UDP會在資料中額外新增一下資料用於區分埠號和差錯檢測欄位,這些資料稱之為 UDP 的頭部。會在下文講。
- 應用層可以很好的控制傳送的時間和傳送的資料。想要用 UDP 實現複雜的功能,給你自由,自定義去吧。
上面這些特點主要是和 TCP 做的對比。
下面我們看一下 UDP 資料包是什麼樣子。
大家可以看到,一個 UDP 資料包除了具體要傳輸的資料之外,額外新增一些資料(首部)。這些資料一共是 8 位元組。所以 UDP 的首部是 8 位元組。前倆個是指明資料包從本機哪個埠來,要到對方電腦的哪個埠。還描述了一下資料的長度,這個長度是包括首部8位元組的,所以為了資料長度欄位能正確表示資料包的長度有限,所以資料部分的長度是有限制的。其實一般情況下,資料部分都不會太大。因為具有簡潔的頭部,所以 UDP 就是一個詞,簡單。
UDP 首部中,源埠號甚至可以省略,省略了代表的含義是我這個資料只需要發出去,不需要回復,因為就算我想回復也不知道你的埠是多少。校驗和欄位可以省略,也就是不做資料的校驗工作,但一般情況都是會校驗一下的。資料校驗是為了防止資料在傳輸過程中出現一些差錯,導致資料不可用,導致殘缺,或者是亂序的,假如將殘缺的資料強行顯示在電腦上就是亂碼,無法使用。如果資料包出現錯誤就會在傳輸層廢棄了,廢棄後會給發鬆資料的主機發一個 ICMP 資料包,告訴他,資料出錯了,然後資料的傳輸過程就結束了!不會在重傳,UDP 不提供重傳的機制。就算重傳了也是應用層控制的,而且是應用層重新構造了一遍資料,再發了一次,UDP 不提供重傳!然後,ICMP 資料包本來想在後面講的,寫不動了。。。下次講。
繼續。
在進行差錯檢驗時,UDP 有一個比較難理解的地方。差錯檢驗的方法這裡不提,網上很多資料,與 IP 協議中差錯校驗方法時一樣的。UDP 的差錯檢驗做到了保證埠到埠的正確傳輸的檢測機制。也能核實出資料部分是否產生了錯誤。所以 UDP 的差錯檢測是傳輸過程中唯一的一次對資料部分進行檢測的過程(TCP也會檢測)。而在進行差錯檢測時,會新增一些額外的資料參與差錯檢測的計算。就僅僅是用一下,計算完校驗和之後就不用了,然後把這個資料遞交給下一層,遞交的資料格式就是上面那張圖片那個樣子,那個才是真正遞交的內容。加入了哪些內容呢,看下圖。
紅色部分稱為偽首部,不要被名字誤導了,它只是拿過來參與運算一下,類似於加密過程中的密碼,計算密文的時候需要用一下,計算完之後還放回去,不會夾在密文中一起傳輸過去。在這裡,差錯檢測實現了一個很有效的功能,可以區分出來資料傳輸的埠是不是正確的,還能區分出來用的是不是 UDP 協議,UDP 協議在協議號欄位的值為 17 (八進位制為)。IPv6 協議下的紅框部分與這個不相同,這樣就查出在 IP 層沒檢測出來的錯誤,這個校驗和保證了整個遞交資料的正確性。
好了,UDP難點講完了,關於UDP的用途就不在說了,關於偽首部這一塊的內容,我發現網際網路上沒有很詳細的資料,也沒有準確的說法,最後我檢視了一下 UDP 的 RFC 文件才明白的。
講一下 TCP
TCP 是一個出色的協議,提供了很多豐富的功能,他是一個點到點的,也就是埠到埠的協議。是一個可靠的,按序位元組流的協議。面向連線的協議。
接收方和傳送方都會有一定儲存空間快取資料,一般用於重傳,分組重組等功能。
面向連線的含義:通訊前必須建立連線。 倆端來維護連線狀態,中間節點不維護。
TCP 實現的功能有提供可靠的資料傳輸,流量控制,擁塞控制。
TCP 實現可靠資料傳輸的功能是基於一種確認(確認:acknowledgement,簡寫成 ACK)機制,也就是說,我給你傳送一個資料包,你要收到了,你一定要告我一聲你收到了,如果你不告我,我就當作沒收到,會再給你發一次,直到你告訴我你收到了為止。就是這樣的一個邏輯,保證了資料的可靠傳輸。
在可靠傳輸具體的實現過程中,TCP 採用的是累計確認,確認的是資料中的位元組號,而不是確認收到了第幾個資料塊兒,正因為如此, TCP 被稱之為按序位元組流的協議。
在 TCP 資料包發出去之後,我等了半天沒有等到你的回覆,我就會在給你發一遍。我到底等多久呢,這就需要仔細考慮了,計算超時的時間有一個演算法,裡面最主要的變數就是 RTT (Round Trip delay Time) ,RTT 指的是我發出去一個報文開始,到接收到確認(ACK)報文之後經過的時間。根據歷史值的平均數,和上一個資料包的 RTT 值,經過加權計算之後的值設定為當前資料包的超時時間。這樣結合了當前網路狀況和歷史網路狀況之後,計算出來的值更加合理。如果我發出去一個報文,超過了超時時間,就會重傳剛剛那個資料包。另一種情況,TCP 有一個滑動視窗機制,因為傳送一個位元組,等待一個確認會嚴重影響資料的傳輸效率的,所以就一段一段的發,確認時,只按照收到了第幾個位元組數來確認。在這一個過程中,會出現一個情況,就是第五段丟失了,第六,七,八段傳過去了被接收了,接收方因為沒有收到第五段的內容,在收到第六段時會傳送序號為第五段首位元組的序號確認(ACK)報文,告知,我這個位元組還沒收到,然後立馬收到第七段,沒辦法啊,在發一個序號為第五段首位元組的序號確認報文,然後立馬又收到第七段。只要連續傳送了 3 個相同序號的確認報文,傳送端會立即重發第五段報文,而不用等到計時器超時之後在重傳。這種機制叫做快重傳。為什麼設定為 3 次重複確認報文就立刻重傳呢,www.zhihu.com/question/21… 這裡有清晰的回答,經過理論證明,3次最好。
TCP 還實現了流量控制的功能,如何實現流量控制的呢,剛剛提到 TCP 有一個滑動視窗的機制。接收方也有一個快取視窗。如果應用資料讀取資料很慢,網速卻很快,資料大量集中,超過了接收方的的快取大小,即便再傳來資料也無法接受,那也只能不斷返回最後一個進入快取區的資料段的確認報文,超過三次,傳送方會不斷的重複傳送相同的資料,造成網路資源的浪費。由此提出了流量控制機制,在每次返回確認報文時,會在報文中加入快取剩餘量是多少。傳送方根據你接收方還能接受到少快取來傳送資料,因為他知道,即便發的多了你也接收不到。其中有一點需要注意,如果傳送方收到接收方的確認報文中,發現快取餘量為 0 該怎麼辦?肯定不能停止傳送,如果停止了就再也無法獲得對方的視窗剩餘數量了,即便對方已經有大量快取的情況下,我沒有給他傳送資料,也就稍帶不回來對方的剩餘快取。所以如果對方快取餘量為0,傳送方會間接性,嘗試性的傳送一組探測報文( ZWP ),探測一下對方還有多少快取,因為這個報文無論是接收,還是被扔掉,都會收到接收方的確認報文,而確認報文中含有快取餘量的資訊,這樣就是實現了 TCP 的流量控制原理。
TCP 實現的另一個功能是擁塞控制,TCP 採用的是端到端的控制方法,就是倆端計算機來控制,中間的路由裝置不提供明顯的控制。TCP 擁塞控制有多種演算法。目前TCP流量控制的流程大概是這樣的。最一開始,採用慢啟動演算法,傳送視窗大小依次為1,2,4,8,16,32,64。當這個視窗到達某一閥值,開始緩慢增加,這裡把閥值設定為 64。開始採用加性增演算法,也就是傳送視窗一個一個的加,65,66,67……,當增加到80時,突發收到三個重複的 ACK,我們認為80報文丟失了,立即乘性減(減一半),視窗降到 40。然後在加性增,一個一個增。假如增加到 50 時,發現一個資料包超時了,這時,直接降視窗大小降為 1,因為此時網路環境極差,為什麼呢,我們知道,收到三次確認報文,說明對方至少收到了三個資料包,只不過資料包是亂序的,還能返回給傳送方三個相同的確認報文,此時網路是較為通暢的,因為只有一個丟了,其他三個都到了,而超時則代表著發出去的包可能都沒有被髮送方接收,在中間路由就被丟棄了,所以此時網路環境很差,需要降低傳送速率,防止擁塞。為什麼會有擁塞機制呢,大家可以想一下沒有這個機制的情況下,中間路由已經不堪重負了,傳送方超時之後還不斷的發資料,所有的主機都不停的傳送資料,因為大家都超時了,都需要重傳,肯定加大擁塞情況啊,就像堵車一樣,明明堵住了,還不斷有新的車排在後面,當然會越來越堵啊,所以才會有擁塞控制機制。然而其他協議並沒有擁塞控制,比如 UDP,ICMP,UDP 絲毫不受網路限制,想發就發,網路堵住了,沒事,反正我不需要保證資料必須到達。而且 UDP 的傳輸速率是沒有限制,所以目前的網路環境中,大量的 UDP 資料包會對網路的產生嚴重的影響。在擁塞控制中,TCP 用到了慢啟動,加性增,乘性減,快恢復的演算法。有需要的可以詳細瞭解一下,這塊內容稍稍複雜,平常網路中,這些演算法都會用到,不會單一的使用具體某一個演算法的。
好了,TCP 的一些功能講完了,我們看一下 TCP 的報文頭。
- 1、源埠號:資料發起者的埠號,16bit
- 2、目的埠號:資料接收者的埠號,16bit
- 3、序列號:32bit的序列號,真正傳送資料時使用,告知當前傳送的一段資料的首位元組的序列號,如果發的報文不含資料(如確認報文),此欄位是沒有變化的。若 SYN=1 或 FIN =1 的資料包當作含有一位元組資料。
- 4、確認序列號:32bit的確認號,是接收資料方期望收到傳送方的下一個報文段的序號,因此確認序號應當是對方剛剛發給你的 TCP 報文中的序號欄位的值再加1,是你的確認序號,你要回復他說,我收到你的資料了,所以要確認的是對方的序號。
- 5、首部長度:4位,最大可表示的數為 15 。而光首部就有 20 位元組,所以在這裡規定,每個 1 代表 4 位元組,就是上圖中的一行,這個欄位值為幾,就是幾行。最大可表示 15*4 = 60 位元組。
- 6、保留:6bit,均為0
- 7、緊急URG:當URG=1時,表示報文段中有緊急資料,應儘快傳送,緊急指標欄位有效。
- 8、確認位元ACK:ACK = 1時代表這是一個確認報文,取值 0 則不是確認報文;
- 9、推送位元PSH:當傳送端PSH=1時,接收端儘快的交付給應用程式;
- 10、復位位元(RST):當RST=1時,表明TCP連線中出現嚴重差錯,必須釋放連線,再重新建立連線;
- 11、同步位元SYN:在建立連線是用來同步序號。一個報文中 SYN=1,ACK=0時,是表示這一個連線請求報文段。SYN=1,ACK=1時表示的是接收方同意建立連線。
- 12、終止位元FIN:FIN=1時,表明此報文段的傳送端的資料已經傳送完畢,並要求釋放傳輸連線。
- 13、視窗:就是提供流量控制的功能,表示可快取位元組數的多少。
- 14、校驗和:該欄位檢驗的範圍包括首部和資料這兩部分。由發端計算和儲存,並由收端進行驗證。
- 15、緊急指標:緊急指標在URG=1時才有效,它指出本報文段中的緊急資料的位元組數。
- 16、選項:長度可變,最長可達40位元組。絕大多數不適用此欄位,也就是說 TCP 絕大部分是 20 位元組。
需要提一下 TCP 校驗時和也需要加入一個偽首部參與運算,TCP 偽首部中協議欄位的值為 6,UDP 是 17,還記得吧。傳輸層的差錯校驗是整個傳輸過程中唯一一次對資料的正確性也進行校驗的。
下面講一下 TCP 的連線機制。
我們知道 TCP 是面向連線的。雙方交換資料前需要先建立連線。TCP 的連線機制就是比較著名的 3 次握手,4 次揮手。4 次揮手指的是 TCP 斷開連線的過程。下面詳細講一下。
3 次握手的過程。
在講握手機制之前需要在鞏固一些概念。序列號,用於標示我傳送的資料的第一個位元組的序號。在建立連線時,會先根據計算機的時鐘資訊初始化一個序列號,比如 5433,這是作業系統規定的,部分資料說是每 4us 就加 1 ,我暫時還沒有了解那麼底層。此時我只是要建立連線,並沒有傳送任何資料。所以我的頭部資訊中的序號就是初始值
1.其中某一端發起建立連線請求:向目標電腦傳送一個建立連線的報文。於此同時,初始化一個序列號(sequence = seq)X ,此時 SYN=1。序列號的初始化是根據系統啟動時間來確定,主要為了防止中間人偽造連線。
2.目標電腦接收到建立請求,決定同意建立請求,返回一個報文。SYN=1 ,ACK =1 。因為 SYN=1,所以目標電腦也需要初始化一個序列號 Y 。ACK=1 代表確認序列號有效。確認序列號的值為 X+1,簡寫成 ACK=X+1。
3.收到目標電腦的確認建立請求報文,向目標電腦傳送一個確認報文。其中 ACK=1 代表時確認報文,確認序列號的置為 Y+1;
需要注意的是,確認號確認的是對方發來的資料我接收到了,所以確認號是根據對方發來的資料包對應的序列號加上資料長度得出的。
倆臺電腦在建立連線時序列號是不同的。建立連線三次握手的過程就是為了溝通交換雙方的序列號。
在詳細說一下序列號這個東西。初始化,是由系統的時間資訊初始化一個值。每傳送出去一位元組的資料,序列號+1 。 其中 SYN =1 時,或 FIN=1 時,序列號也 +1 。序列號相對於初始化的序列號的偏移量就是發出去的位元組數再 -1 。因為建立連線時,多加了一個 1 嘛。確認號呢,就是傳送過來的資料包對應的序列號加上資料長度的值。每一端都有自己的序列號。好了,講清楚了吧。
為什麼是三次握手,而不是倆次握手呢。
因為有可能出現這樣的情況,我想和你建立連線,SYN=1 seq=4444 ,但是這個資料包過了很久都沒有傳到你那邊。我以為這個資料包丟失了。於是重新發起建立連線的請求 SYN=1 ,ACK=0 ,seq=5555。但是之前seq = 4444 的報文居然到你那兒了,你回覆了一個同意建立請求(SYN=1,ACK=4445)。這是倆次握手,出現了問題,我的 seq 不匹配。我要傳送資料的話 seq 的值是根據 5555 來相對計算的,而不是 4444 ,雙方交換資料就會出現錯誤了。所以必須三次握手,你收到了 ACK=4445 的確認報文,你知道這是過期了的請求,不去處理他,繼續等待 ACK=5556 的確認報文,等到之後,回覆一個 ACK=Y+1 的確認報文,至此,你倆交換了 seq 值,就可以開始傳輸資料啦。
下面是四次揮手的機制。
我準備斷開連線了,傳送 FIN=1 seq = 2222 ACK=1( 無論是否確認過上一個報文,這裡 ACK 都要為1。官方文件中規定,除了建立連線時,ACK 為 0 以外,其他所有的報文 ACK 都為 1,包括斷開連結時的報文,因為網路不保證確認報文的穩定交付,所以這裡索性加上ACK,反正這個欄位不用也是浪費。 )
1 我一但發出 FIN 報文,我就不會在給你傳送任何有資料的報文了。
2 你收到了我的FIN 報文。回覆一個確認報文 (ACK=2222+1)。
此時,你雖然應答了我的斷開連線報文,但是你仍可以繼續傳輸資料。比如你的資料還沒有穿完我就發給你一個斷開連線的報文,如果你沒有傳完資料,是可以繼續一直傳的。
你傳一個資料,我返回一個確認報文,你傳一個,我返回一個。終於傳完了,你覺得可以斷開連線了。
於是,3 你給我傳送一個 FIN 報文。
我收到你的FIN報文,4 返回給你一個確認報文。如果你收到了確認報文就可以立馬斷開連線了。而我不行,我擔心確認報文會在路上丟失,丟失了的話你會重傳FIN報文的。如果在一段時間(2倍的資料包最大存活時間)後,沒有收到你重傳的FIN報文,我也就斷開連線了。
四次揮手的原因是 TCP 是全雙工的,就是你可以給我發資料,我也可以給你發資料。四次揮手的原因是,任何一方都需要斷開連線,也就是倆次揮手的記錄,A 傳送 FIN,B 返回 ACK,就代表 A 已經確定了 B 收到了自己的斷開連線請求了。同理 B 也一樣,也需要獲得斷開連線的確認報文。所以一共是四次揮手。
還需要注意的是,即便 A 申請斷開連線,B 仍可以繼續傳送資料。還需要注意的是,四次揮手可以簡化成 3 次揮手,就是 A 申請埠連線,B 沒有要發的資料,直接返回一個帶FIN的確認報文,A 在返回一個確認報文,雙方就都斷開連線了。其實也是四次,只不過中間兩部放在一個報文中了。
為什麼要四次揮手呢?
沒有那麼多為什麼,就得四次啊,一邊傳送一個 FIN 報文和確認報文,加起來四個。三個可以嘛,可以啊,上面不是說了麼。我的意思是說,能不能少一個確認,當然不行了啊,少一個確認我怎麼知道你收沒收到我的斷開連線的報文啊。嗯。就這樣回答就行了。
其實更有意義的問題是,主動斷開請求的一方為什麼需要等待倆倍的TCP中最大報文段生存時間(MSL ),也就是2MSL(Maximum Segment Lifetime)。
等待 2MSL 時間主要目的是怕最後一個 ACK 報文對方沒收到,那麼對方在等待你的 ACK 報文超時後將重發第三次揮手的 FIN 包,主動關閉端接到重發的 FIN 包後可以再發一個 ACK 應答包。在 TIME_WAIT 狀態時兩端的埠不能使用,要等到 2MSL 時間結束才可繼續使用。當連線處於 2MSL 等待階段時任何遲到的報文段都將被丟棄。TTL 與 MSL 是有關係的但不是簡單的相等的關係,MSL 要大於等於 TTL。
網路層
網路層的核心功能是 轉發與路由
。網路層包含很多協議,因為網路層要實現路由和轉發的功能,所以在這一層使用了很多路由協議,我們要實現轉發功能,就得知道轉發到哪,也就是得有地址,就要用到 IP 協議。IP 協議中規定一些定址規則,資料包過大如何切割成一小片一小片的問題。此外,網路層還包括重要的 ICMP 協議,依託於這種協議的資料都像是網路中的指令,計算機或者中間路由裝置就成為了官兵的角色,根據指令內容做出相應的動作反應。
下面講一下 IP 地址的相關內容。
我們知道 IP 地址的長度是有限的,只有 32 bit,數量也是有限的,所以需要對其進行劃分,比如一部分給美國使用,一部分給中國使用。劃分成大塊之後,還需要繼續劃分,劃分成更小的省市,或者企業,或者政府機關。這個劃分的過程,稱之為劃分子網。於是就規定了, IP 地址是由兩部分組成 網路號和主機號
。但是網路號的位數不確定,若一個子網很大,他的網路號的位數就很少,那麼主機數就多一點,子網中的主機就可以很多。舉一個例子,中國聯通擁有一個大的子網,所以他的網路號位數就會很少,比如有 4 位,那麼主機號就剩下 28 位,那麼就可以標識將近 2^28 個主機,那麼屬於中國聯通的各個城市的網點,馬路邊的路由器,所有使用聯通網路上網的裝置,都屬於聯通子網內的主機。當然這樣的描述極其不準確
,主要是為了大家方便理解網路號和主機號的概念。
在很久之前,我們將網路地址分為五類網路地址,A,B,C,D,E類網路地址,但是對於現在來說,早已不在使用,這樣劃分方式造成了 IP 地址的極大浪費,所以現在幾乎不用有類地址來劃分子網。現在 IPv4 的 IP 地址早就已經分配完了,但是通過 NAT 技術(網路地址轉換技術),還能保證每一個上網裝置能有一個 IP 地址。NAT 就不講了,有一點點複雜。。由於物聯網的快速發展,網路資源的普及,上網裝置越來越多,所以國家正在大力推行 IPv6 的發展。很多人這樣描述 IPv6 的地址數量,可以給地球上每一粒沙子分配一個 IP 地址,更不用說裝置了。滑稽一下,推進 IPv6 有利於實名制。咳咳,繼續講。
現在劃分子網的方式是 CIDR (無類域間路由),形式是 x.x.x.x/n,例如 202.113.132.45/24,表示前24位網路號,那麼後 8 位就是主機號了。現在劃分子網的方式就是按照上述的規則來劃分的。有興趣的同學可以自己去了解子網劃分的具體過程。
子網的劃分根據個人興趣去了解,下面的內容希望大家都能理解,很有用,一些特殊的網路地址:
這些特殊地址可以先做一個瞭解,以後在專案中會經常遇到,那時候就理解的具體的含義和概念。
下面是一些私有的網路地址,一般不用於在公網上註冊,也就是說,一般訪問下面的網址都是在自己的一個小區域網內訪問資料:
RFC1918 規定區塊名 | IP地址區段 | IP數量 | 分類網路說明 | 最大CIDR塊 (子網掩碼) | 主機端位長 |
---|---|---|---|---|---|
24位區塊 | 10.0.0.0 – 10.255.255.255 | 16,777,216 | 單個A類網路 | 10.0.0.0/8 (255.0.0.0) | 24位 |
Shared Address Space | 100.64.0.0 - 100.127.255.255 | 4,194,304 | 64個連續B類網路 | 100.64.0.0/10 (255.192.0.0) | 22位 |
20位區塊 | 172.16.0.0 – 172.31.255.255 | 1,048,576 | 16個連續B類網路 | 172.16.0.0/12 (255.240.0.0) | 20位 |
16位區塊 | 192.168.0.0 – 192.168.255.255 | 65,536 | 256個連續C類網路 | 192.168.0.0/16 (255.255.0.0) | 16位 |
瞭解即可。
好了,網路方面的知識普及結束,下面看一下資料方面的格式要求。從傳輸層傳來的 TCP or UDP 的段需要在網路層新增一個首部,然後再提交給資料鏈路層。網路層對資料做了什麼事情呢,首先網路層是有轉發功能的,轉發需要知道目的地,於是網路層對資料加入了目的地址,目的地址就是 IP 地址,唯一的標示一臺主機。網路層還會對超過 MTU ( Maximum Transfer Unit 最大傳輸單元)的資料包進行分片。乙太網的 MTU 是 1500 位元組,如果這個值太小,一個資料包會被分隔成很多塊,如果這個值過大,傳輸的延遲就會增加,資料包的錯誤率也會增加,最終選擇為 1500 位元組,屬於規定的一種吧。好了,如果傳輸層要傳輸的資料大於 MTU 那麼這個資料在網路層就會被分片。下面看一下 IP 資料包的首部:
版本 4bit 指 IP 協議的版本。目前廣泛使用的 IP 協議版本號為 4 ,IPv6,版本號為 6 。
首部長度 4bit 單位是 4 個位元組( 32bit 也就是上面的一行。因為 4bit 最大描述的數值是15,首部長度最短也需要20位元組,不夠描述的,所以單位改成 4 位元組),那這裡的值一般為 5 。首部長度也就是 20 位元組。首部長度大小取值範圍 20 位元組到 60 (4*15)位元組,首部長度一般都是 20 位元組。當 IP 分組的首部長度不是 4 位元組的整數倍時,必須利用最後的填充欄位加以填充。因此資料部分永遠在4位元組的整數倍開始。
區分服務 8bit 目前幾乎不使用,若要支援區分服務,需要中間所有的路由器都支援區分服務,但是目前的絕大多數路由器都不支援,所以區分服務幾乎不使用了。
總長度 16bit 首部長度 + 資料長度,單位是位元組。資料包的最大長度為 2^16-1 = 65535 位元組。但由於資料鏈路層的最大傳送單元 MTU < 65535,所與當一個資料包封裝成鏈路層的幀時,此資料包的總長度(即首部加上資料部分)一定不能超過下面的資料鏈路層的MTU值,若超過則需要分片(因此有上圖的第二行,用於切片)。
標識 16bit 軟體在儲存器中維持一個計數器,每產生一個資料包,計數器就加1,並將此值賦給標識欄位。但這個“標識”並不是序號,因為 IP 是無連線服務,資料包不存在按序接收的問題。當資料包由於長度超過網路的 MTU 而必須分片時,這個標識欄位的值就被複制到所有的資料包的標識欄位中。相同的標識欄位的值使分片後的各資料包片最後能正確地重灌成為原來的資料包。
標誌 3bit 目前只有2位有意義。
標誌欄位中間的一位記為 DF (Don’t Fragment),意思是“不能分片”。只有當DF=0時才允許分片,若為 1 則不分片
標誌欄位中的最低位記為 MF (More Fragment)。MF=1即表示後面“還有分片”的資料包。MF=0表示這已是若干資料包片中的最後一個。
片偏移 13bit 當一個較長的 IP 資料包在分片後,分片後的某一片的第一個 bit 在原分組中的相對位置。也就是說,相對傳輸層資料包的資料部分的起點,該片從何處截斷的。片偏移以 8 個位元組為偏移單位。這就表明,每個分片的長度也一定是 8 位元組( 64位 )的整數倍。那為什麼 ipv4 片偏移量以 8 位元組位單位呢?如果直接用位元組為單位,總長度是 16bit 而片偏移量只有 13 bit,就會出現不夠描述的。 所以偏移量就按 8 位元組為單位的來規定了。
生存時間 8bit TTL( Time To Live ) 就是說 IP 資料包最大能經過多少個路由器跳轉(每經過一個路由器,TTL減1)。防止資料包在網路中因為某些錯誤導致無限迴圈,浪費網路資源。那麼可以知道,路由器在轉發時會將這個欄位的值減 1,並重新計算首部校驗和。
協議 8bit 標誌此資料包攜帶的資料是使用協議型別( 例如TCP、UDP等 ),以便使目的主機的 IP 層知道應將資料部分上交給哪個處理過程。
首部檢驗和 16bit 注意這個欄位只檢驗資料包的首部,不包括資料部分。這是因為資料包每經過一個路由器,路由器都要重新計算一下首部檢驗和(因為生存時間、標誌、片偏移都可能發生變化,就是說在中間路由被重新分片時會出現變化。),不檢驗資料部分可減少計算量。
源IP地址 佔32位。
目的IP地址 佔32位。
上述就是 IP 資料包的頭部。
下面講一下 DHCP(Dynamic Host Configuration Protocol)。
我們知道,設定自己電腦 IP 地址有倆種方式,第一種是直接設定一個固定的 IP 地址,同時需要配置一系列複雜的資訊,比如子網掩碼,DNS 伺服器地址等等。這很麻煩,而且需要具備相關的專業知識。自己以前就是看著教程配置,什麼都不懂,最後設定的半天也連不上網,索性直接選擇自動獲取 IP 地址了。
自動獲取 IP 地址使用的就是 DHCP(動態主機配置協議)。通過向本網路中的 DHCP 伺服器發出請求,DHCP 伺服器看看自己還有那些 IP 地址沒有分配出去,然後隨便挑一個給你。你就有了 IP 地址。DHCP 伺服器不僅僅給你提供 IP 地址,在分配 IP 地址時,連帶著子網掩碼,預設閘道器地址,DNS伺服器地址都裝在一個資料包中發給你了。
動態獲取 IP地址有什麼好處呢,第一就是簡單,插上網線就能用,不用使用者自己去配置。第二個就是 IP 地址可以重複使用,你的電腦關機了,我就把這個 IP 地址給別人用。
下面講一下如何和 DHCP 伺服器溝通索要 IP 地址。
- 想上網的裝置先向本網路中廣播一個 DHCP 報文,看誰回應,誰就是DHCP伺服器。這個過程稱之為 傳送發現報文
- DHCP伺服器在自己身體裡找一個合適的 IP 地址,然後向全體廣播出一個提供報文。
- 向上網的裝置會再傳送一個請求報文,去廣播詢問能不能用某個IP地址。
- DHCP 伺服器傳送確認報文,來響應這個 IP 到底能不能用。
為什麼這麼麻煩呢,主要的原因是一個網路中可能有多個 DHCP 伺服器,當有裝置請求 IP 地址時,所有的 DHCP 都收到廣播,都會給他發了一個自認為合適的 IP 地址的。上網裝置收到很多 IP 地址,會隨便挑一個 IP 地址,再廣播一遍,這個 IP 地址能不能用,剛剛提供 IP 地址所有 DHCP 都確認一下這個 IP 是不是自己的,如果是自己的就回復一個確認報文,然後更新一下自己的資料庫,這個 IP 地址已經有人用了。如果不是自己的 IP 地址就返回一個錯誤,收回自己剛剛分配出去的 IP 地址。
具體的通訊過程如下:
- DHCP伺服器的預設埠是 67
- 聯網裝置傳送 DHCP 報文的預設埠是 68
- 發現報文:源 IP 0.0.0.0:68 目的 IP 255.255.255.255:67 雙方都不知道彼此的 IP 地址 所以都是廣播通訊
- 提供報文:源 IP x.x.x.x:67 目的IP 255.255.255.255:67
- 下面就是再發一個請求報文和確認報文了。
確認報文中包含有 IP 地址,子網掩碼等資訊,還有一些資料,比如 IP 地址的過期時間,方便回收 IP 地址。因為不可能分配出去不回收吧,當 IP 地址過期前,上網裝置會再使用 DHCP 來續租 IP 地址。
DHCP 報文有如下特點:
- 在應用層實現,通過應用程式來實現
- 使用 UDP
- 使用了 IP 廣播
看起來這個協議簡單又有趣。
在看一個有趣的 ICMP(Internet Control Message Protocol)協議。
ICMP是網際網路控制報文協議。
與 IP 息息相關。它的主要功能有差錯報告和網路探測倆種。差錯報告就是資料在網路中進行轉發時,可能會出現一些錯誤,比如,資料包經過路由器時發現 TTL=0,那麼路由器就會丟棄該報文,然後向傳送端傳送一個 ICMP 報文。而網路探測的功能,最常用的就是 ping 工具呢,這個 ping 工具基於的就是 ICMP 的回聲請求應答報文,屬於 ICMP 中的一種報文格式。根據 ICMP 的功能,ICMP 報文也分為了兩大類。
差錯報文:
目的不可達報文: 比如目的網路不可達,目的埠不可達等問題,會傳送這類報文。
源抑制:就是當路由器快取滿了之後,路由器會向源主機傳送抑制傳送速度的報文,來進行擁塞控制,但是目前使用的是別的擁塞控制方法,這種報文就很少用了。
超時/超期報文:最典型的中間路由裝置的數量超過了 TTL 的值。
引數錯誤:如資料包頭部有問題時,會傳送這類報文。
重定向:當目的網路不是我這個路由器轉發的地址,而是我旁邊另一個路由器轉發的地址,就會給源主機傳送一個這樣的報文。
第二類是網路探尋的報文:
回聲請求與應答報文:ping
時間戳請求與應答報文:用於獲取時間戳。
其實 ICMP 的報文型別挺多的,也不容易理解,大家淺嘗輒止就好,不用過於深究,只要記住網路上傳輸過程中很多的報告類資訊,一般都基於 ICMP 就好。
下面有一些不傳送 ICMP 報文的情況:
- 如果傳送的本來就是 ICMP 報文,而資料包出現了錯誤,此時不會在傳送了,如果傳送的話容易死迴圈。
- IP資料包分片只對第一個分片傳送 ICMP 報文,其他的分片不發。比如出現目標網路不可達的錯誤,第一個問題,那後續所有分組的報文都是往同一個網路傳送資料,為了減少重複,減少網路的消耗,一般只對第一個分片傳送。
- 使用多播 IP 地址的資料包不傳送 ICMP報文,因為這是面向所有主機傳送的廣播,容易引起大範圍的錯誤報文,所以規定這種型別的資料包不傳送 ICMP 報文。
- 特殊 IP 地址的資料包不傳送 如 127.0.0.0 或 0.0.0.0;
目前隨著網際網路的發展,還有幾種 ICMP 報文已經不在使用了,比如:
資訊請求於應答報文。
子網掩碼請求和應答報文。
路由器詢問和通告報文
下面看一下 ICMP 的神奇用法 Tracerout,用來探測一個資料包傳送到目的主機所經過的路由器資訊。
traceroute 探測路由路徑
第一組 IP 資料包將 TTL 設定為 1 ,那麼到達下一個路由肯定會被丟棄,路由器會發一個 ICMP 報文,然後來測量往返時間
第二組 IP 資料包 TTL=2
...
….
…….
停止條件:traceroute 在封裝 UDP 報文時使用的是一個幾乎不會用的埠號,到目的主機時,目的主機會返回一個埠不可達的錯誤報文。。這樣就探測了完整的路由路徑。測量路由時,一組會發三個資料包,防止出現問題。
好了,網路層的相關知識也講完了,不知道大家看的過癮不過癮,可以進行溫習,反覆的看,有什麼問題的朋友可以和我多多交流。微信 WytheHard。
歡迎關注公眾號: 吳先生的伊甸園