計算機網絡知識隨記之基礎篇

原文連接
本篇主要講解網絡的基礎知識,從tcp/ip協議棧講起,不會講解的太深,權做本身的筆記,有問題的地方但願你們能夠留言指出。
php

協議棧

這裏主要講解TCP/IP協議棧,再也不講解其與OSI協議棧的區別。html

TCP/IP協議分爲4層,由上至下爲應用層、傳輸層、網絡層和網絡接口層。java

  • 應用層:定義上層應用能夠直接使用的高級協議,如http、ftp等。
  • 傳輸層:定義控制數據傳輸的協議,用以保證數據的可靠性和順序到達性等,如tcp、udp協議。
  • 網絡層:定義不一樣網絡類型間通訊的協議,如IP協議用於實現網際路由,ICMP協議用於檢測網絡的暢通性,ARP協議用於獲取設備MAC地址等。
  • 網絡接口層:定義網絡介質上的傳輸協議,和電氣相關,如Ethernet協議、802.3協議等,主要由操做系統的網卡驅動程序實現。

四層由上到下逐層依賴,用戶數據也在每一層被添加進不一樣的頭部,以便進行傳輸和解析,封裝過程大體如圖:
network-protocol-all.png
圖1linux

下面我自上而下詳細講解下每一層的做用和數據封裝內容。算法

應用層

該層主要涉及一些高級協議,像你們耳熟能詳的http協議、ftp協議等等,每個協議都能單獨開幾篇文章來說了,這裏就不贅述了。編程

傳輸層

該層主要是對應用層的數據進行分割和重裝,提供端到端的服務。主要有UDP和TCP兩種協議,下面分別講解這兩種協議。緩存

UDP

UDP即User Datagram,用戶數據報協議,從名字上會聯繫到IP Datagram,即Ip數據報,二者也確實有關聯。在UDP被併入TCP/UDP協議簇以前,是做爲IP協議的上層抽象存在的,它的名字也是源於IP Datagram,前面加上User便有了端到端的意味。圖2展現了UDP的包結構。安全

network-udp-protocol.png
圖2服務器

UDP的首部只佔用8個字節,頂部的12個字節的僞首部是用於計算UDP首部檢驗和。所謂「僞」是指這個首部只是拿來用下,並不出如今實際傳送的數據中。UDP能夠依據僞首部計算出來的檢驗和來判斷數據包是否正確到達目的地,即若是傳輸過程當中該數據包中僞首部設計的數據發生變化,那麼該數據包會被視爲非法包,被丟棄。cookie

IP地址不是在IP層獲取的嗎?而它對上層的UDP不是應該透明嗎?
分層只是一個邏輯上的理想化模型,在實際實現中,爲了效率等,UDP的檢驗和其實就是經過IP層來計算獲取的。

UDP是面向報文的,沒有可靠性控制,沒有擁塞控制,無鏈接,因此其開銷小,但網絡環境差或者發送數據過大時致使ip分片過多,致使發送率下降,影響程序的使用。爲了提升UDP的發送率,應該儘可能使得UDP可使用一個IP數據報就能發送出去,這裏就涉及到MTU(下文會詳細講解)。

UDP推薦傳送的數據大小爲1500(以太網的MTU)-20(ip數據報首部)-8(udp首部)=1472字節。

TCP

TCP即Transportation Control Protocol,傳輸控制協議,提供了一種可靠的面向鏈接的字節流傳輸層服務。TCP協議相對複雜,主要知識點有三次握手創建鏈接、四次握手關閉鏈接、滑動窗口協議、擁塞控制策略、Nagle算法等等,這裏只作簡單介紹,基本一帶而過,感興趣的能夠去詳細研究。圖3展現了tcp報文段(segment)的格式。
network-tcp-protocol.png
圖3

源端口號和目的端口號是用於區分進程用的,序號和確認號是用於握手的憑證。4bit的首位長度(單位是4Byte)代表tcp首部最大爲15*4Byte=60byte。後面的標誌位有幾個要講解下:

  • ACK: ACK=1表示該報文段中有確認號須要處理。
  • PSH: PSH=1表示該報文段中有數據須要處理。
  • RST: RST=1表示到目的端的鏈接出問題了,須要上層作出處理,如從新創建鏈接等。
  • SYN: SYN=1 ACK=0代表是創建鏈接請求報文段,SYN=1 ACK=1代表贊成創建鏈接報文。
  • FIN: FIN=1表示對端的數據已經發送完畢,要求釋放鏈接。

窗口大小是用來實現滑動窗口協議的,用來加快tcp數據的傳輸。

下面咱們來看看創建鏈接的示意圖。

network-tcp-connection.png

三次握手的過程上圖已經詳細地描述了,這裏說一下第三次握手的必要性,即發送方在收到接收方的ack後又主動發送了一次ack給接收方。緣由是爲了不一種異常狀況:在網絡不穩定的狀況下,發送方發出的一個鏈接請求通過在某個網絡中間節點滯留,等其到達接收端時正常的通訊早已結束,但接收方不知道,因此它會馬上發送一個ack給發送方,若是此時沒有第三次握手的確認,那麼服務端會認爲該鏈接有效,形成資源的浪費。

下面再看一下關閉連接的示意圖。
network-tcp-fin.png

四次握手的過程上圖已經詳細的描述了,這裏說一下TIME_WAIT這個狀態,爲何會有一個2MSL的時間存在?MSL 即Max Segment LifeTime,一個報文段的最長生存時間。2MSL即是用來保證A發送的最後一個ack能夠到達B,若是沒有到達B,B會超時重發ACK和FIN報文,此時A也能夠收到該報文,而後從新發送ack,以保證四次握手的完整性。固然關於TIME_WAIT是許多服務器運維人員的心頭痛,由於它會佔據着一個端口不釋放,浪費資源,後面再寫文章專門聊下TIME_WAIT吧。

TCP的狀態遷移見下圖。

network-tcp-status.png

上面這張圖基本闡述了每一個狀態的遷移時機,這是一個有限狀態機表現方式。看懂了這個圖,tcp也算是入門了。

關於傳輸層想了解更多的話,能夠好好閱讀下這篇文章

網絡層

網絡層也常叫IP層,由於這一層最重要的協議就是IP協議,用於網際路由,提供不可靠無鏈接的數據報傳送服務,該層的數據傳送單位爲IP數據報(IP datagram)

其協議格式以下圖所示。
network-ip-protocol.png

這裏簡單介紹幾個部分。

  • 首部長度同tcp同樣,最大表示15*4Byte=60Byte。
  • 總長度(16bit)是指整個ip數據報的長度,表示最大值爲64K,也就是說IP報是有最大傳輸限定值的。
  • 標識字段用於惟一的表示一次數據傳輸。
  • 標誌位(3bit)用於表示是否能夠分片或者是否有其餘分片。
  • 片偏移(13bit)用於切片時的片偏移。

這裏詳細講解下IP分片。IP數據報下一層要經過數據鏈路層封裝成幀發送出去,但幀大小受網絡設備電氣屬性影響是有限制的,即MTU,不一樣的網絡拓撲和設備的MTU也不一樣,如以太網是1500字節。若是IP數據報大於MTU,那麼它必須分片才能實現數據的傳送,分片發生在各個網絡設備上,在目的主機參照標識字段、標誌位和片偏移來實現重組。值得注意的是,ip分片傳輸後,標識字段都被複制到每個分片上,而總長度也變爲該片的長度。優勢是IP數據報能夠穿過複雜多變的網絡環境,缺點是一個分片丟失,該數據報就發送失敗,增大了丟包的機率。

網絡層另外一個重要的協議是ICMP(Internet Control Message Protocol)協議,其提供了一套查找網絡故障的機制,有差錯報文和控制報文,可用於檢查主機不可達、中間路由出問題、網絡擁塞等問題,不一樣的問題會返回不一樣的錯誤類型。ICMP的功能只是報告問題而不能糾正錯誤。ping命令使用控制報文中的回顯請求和應答來實現的,traceroute是使用差錯報文中的TTL超時報文和目標不可達報文實現的。

網絡層還有ARP和RARP協議用於在IP和MAC地址之間轉換,這裏就不詳細講解了。

另外這篇文章詳細地講述了IP數據報分片是如何在局域網和廣域網中傳遞的,感興趣的能夠去詳細看下。

網絡接口層

tcp/ip的網絡接口層對應OSI模型中的鏈路層和物理層,其傳輸的數據單位是幀(Frame)。在上層要發送的數據包的首部和尾部添加相關數據後封裝成幀後發送出去, 其主要有以下三個做用:

  1. 爲網絡層接收和發送ip數據報(IP datagram)。
  2. 爲arp發送請求和接收數據。
  3. 爲rarp發送請求和接收數據。

網絡接口層協議有Ethernet協議、802.3等等,以下圖展現了這兩種協議的組成部分。
network-link-protocol.png

該層首部一般包含目的和源地址,就是設備的mac地址,尾部是一個CRC校驗碼,用於保證數據的準確性。詳細介紹能夠去參考這篇文章

細心的同窗應該觀察到中間傳輸的數據域是有長度限制的,如802.3是從38到1492,ethernet協議是從46到1500。這裏數據長度的限制是由傳輸介質的物理特性決定的,若是傳輸的數據長度不在該範圍內,則該幀會被丟棄。這裏就是MTU的出處了。

MTU(Maximal Transmission Unit),最大傳輸單位值,便可以一次性傳輸數據的最大值,該值每每受傳輸介質約束。在以太網協議中,MTU值爲1500.一個幀的構成爲7字節前導同步嗎+1字節幀開始定界符+6字節的目的MAC+6字節的源MAC+2字節的幀類型+1500+4字節的FCS(校驗碼),最大值爲1526。其實經過抓包工具獲取的一個幀最大倒是1514,緣由在於數據幀到達網卡後,在物理層網卡會先去掉前導同步碼和幀開始定界符,以後根據FCS進行驗證,若是失敗則丟棄該幀,不然就檢查目的MAC地址是否符合本身的接收條件(MAC地址和本身的匹配、廣播地址等等),若是符合,將該幀交設備驅動程序作進一步的處理,這個時候抓包工具才能抓到包,此時的幀也被去除了校驗碼,因此最終抓到的幀大小爲6+6+2+1500=1514。傳輸數據有最大值也有最小值,當上層傳輸的數據小於最小值時(ethernet時46),好比tcp三次握手時的ack返回僅有20(tcp頭部)+20(ip頭部)=40字節,小於最小值46字節。對於此種狀況,網卡驅動程序會進行自動填充,但若是抓包工具若是先於驅動程序抓到該幀,那麼其大小就要取決於抓包工具自己的顯示,wireshark只是顯示原大小。

上面對於鏈路層中的包長度進行了簡單地說明,感興趣的同窗能夠去查看這篇文章

至此,TCP/IP的網絡協議棧大體簡單地進行了講解,下面就再以問答的形式講解幾個知識點。

MTU是什麼?MSS是什麼?二者有什麼關係嗎?

答:MTU即Maximal Transmission Unit,最大傳輸單元,是指網絡接口層中因爲受傳輸介質的物理特性制約一次能夠傳送的最大字節數,如以太網中MTU爲1500Byte。MTU能夠由人爲修改,從而達到最優網絡傳輸效率。
MSS即Maximal Segment Size,最大段長度,是傳輸層中TCP報文段中數據段的最大長度,默認是536Byte。其存在的意義以下:

  • 防止傳輸數據太小形成資源浪費。好比發送數據每次都是1Byte,在每一層都會添加首部(20Byte的TCP首部和20Byte的IP首部),以後纔會完成發送。也就是說傳送的41Byte中,只有1Byte是有效數據,這明顯形成了資源的浪費。
  • 防止傳輸數據過大形成傳輸效率下降。若是發送的數據過大,超過了MTU的值,那麼在IP層就會出現分片現象,而接收方也要耗費更多的資源和時間來處理分片,若是在傳輸過程當中發生丟片,也會進一步增大網絡開銷。

有了MSS,TCP每次傳輸的數據都能被控制在一個合理的範圍內,避免IP分片的發生,增大傳輸效率,最大化利用資源。

下圖描述了MTU和MSS的關係,能夠簡單地理解爲MTU=IP Header + TCP Header + MSS。
network-mtu-mss.jpg

下面這張圖描述了TCP是如何在三次握手創建鏈接的過程當中和對方協商MSS大小的。注意MSS只會出如今SYN包中。

network-tcp-mss.jpg


什麼是半鏈接?SYN Flood是怎麼一回事?
答:所謂半鏈接,顧名思義就是尚未完成三次握手創建鏈接。具體是指服務端在收到客戶端的SYN包後,會據今生成一個半鏈接的對象,並將其存儲在一個半鏈接隊列(SYN Queue)中進行維護。一旦收到客戶端的ACK包後會將該對象從半鏈接隊列(SYN Queue)轉移到已鏈接隊列(Accept Queue)中等待accept系統調用。一經accept調用,數據的傳輸和接收纔會正式開始。隊列確定是不能無限長的,由於每一個對象都會佔據存儲資源,內核配置中有兩個參數對應這兩個隊列的長度。

  • /proc/sys/net/ipv4/tcp_max_syn_backlog,該參數指定了半鏈接隊列的最大長度,隊列達到該值時,後續的SYN請求會被拒絕。能夠經過sysctl進行配置。
  • sk_max_ack_backlog,該參數指定了已鏈接隊列的最大長度,隊列達到該長度時,後續的鏈接請求會被拒絕。該參數通常是在listen系統調用中指定的,如listen(int sockfd,int backlog) 中第二個參數。

SYN FLood攻擊即是利用了半鏈接隊列的長度限制來完成攻擊的。攻擊策略爲僞造大量SYN包發送給服務端,但不返回ACK包,致使服務端半鏈接隊列被迅速佔滿,正常的鏈接被拋棄。服務端會有大量處於SYN_RECV狀態的鏈接,其會嘗試重發ACK包給實際不存在的客戶端,致使CPU滿負載,內存耗盡,從而達到攻擊效果。

通常能夠經過修改net.ipv4.tcp_synack_retries 來減小重試發送ack的次數、開啓 net.ipv4.tcp_syncookies、調大net.ipv4.tcp_max_syn_backlog來進行防護。

下面兩篇文章對半鏈接都進行了很詳細的講解,感興趣的能夠去查看:文章一文章二
阿里雲在2014年春節期間遭遇過SYN Flood攻擊,感興趣的也能夠去看下相關介紹


什麼是粘包?怎麼解決?
答:粘包是指服務端收到的數據出現不完整、混亂等狀況。

粘包出現的緣由是TCP數據的「流」特性。所謂「流」是指tcp傳輸的數據並不會在邏輯上進行劃分,好比客戶端給服務端發送了A和B兩份數據,tcp發送的時候並非分兩次先發A數據,再發B數據,而是可能出現下面的狀況:

  1. 先發送A和B的一部分,再發送B剩餘部分
  1. 先發送A的一部分,再發送A剩餘部分和B

可能有人會問,咱們在編程的時候不是直接調用了send(byte[])了嗎?爲何數據會出現這種混亂的發送狀況,緣由有兩個:

  1. 若是開啓了Nagle算法,即tcp_no_delay是false,那麼即使調用了send方法,tcp也不必定會當即將數據發送出去,而是會等待其餘數據一同發送,這樣作是爲了最大化利用網絡資源。
  1. 若是發送的數據過大,超過了MSS的大小,tcp會對數據進行分段發送,此時也不可能一次性將數據發送完畢。

這個時候服務端接收數據時該如何把A和B區分開呢?若是隻是單純地將一次接收的數據做爲完整數據(A或B)處理,很容易出錯。這裏就引出了封包和拆包的概念。

所謂封包,就是將要傳送的數據按照一個可辨識的結構傳輸,好比客戶端和服務端約定傳遞的數據都聽從 字節長度(4Byte)+實際數據 的規則,那麼服務端在解析數據(即拆包)的時候,就能夠先讀取4Byte,獲取到一個完整數據的實際長度,而後再讀取該長度的數據,以後再交由上層處理。
關於封包和拆包,感興趣的能夠去看下面這篇文章


socket的close和shutdown有區別嗎?

答: 有區別。

這個涉及到tcp關閉鏈接的方式,主要由兩種:

  1. 一種是正常的經過4次握手關閉,這是優雅的方式,能夠保證雙方的數據都被接受。
  1. 一種是經過發送RST包的方式跳過4次握手快速關閉鏈接,這是暴力的方式,不安全。

close會直接關閉socket的文件描述符,包括輸入和輸出流,但shutdown只是在己方的輸入輸出流上作了一些人爲的處理,好比丟棄了輸入流中的數據,直接返回EOF結束符和向輸出流寫入東西時報錯等等,但並不會關閉socket,因此對方的鏈接依然能夠正常讀寫,要關閉仍是要調用close。

另外close只會關閉本進程的socket文件描述符,若是其還被其餘進程調用,則實際不會被關閉。但shutdown卻會影響全部使用了該socket的進程。無特殊狀況,使用close關閉socket便可。
在shutdownOutput後,再寫入時會報broken pipe的錯誤。對執行了close的socket讀入時會報錯:socket closed。詳情能夠參考這篇文章


flush有必要嗎?

答:基本無必要。

咱們在使用文件流的時候,都會強調一下flush函數,有時若是不主動調用,會致使寫入的數據沒有寫到磁盤上,形成數據丟失。那麼socket編程中是否要注意flush呢?

實際上當咱們socketOutputStream.write(byte[])(java接口)的時候,數據已經發送出去,與flush無關。固然若是你在outputstream的外層套一層buffer,好比在SocketOutPutStream外層包一個BufferedOutPutStream,其內部會有本身的緩存,這個時候flush就有必要了,由於buffer至關於在jvm層作了一層緩存,直到調用flush函數,數據纔會被寫出。


進程在乎外中止的時候,其關聯的socket會關閉嗎?

答:會關閉。

在linux系統中,是回這樣的。當進程意外停止或者被kill的時候,系統會直接調用相關socket的close函數,單方面關閉socket連接而且不等待對方關閉,會直接給對方發送RST包。能夠參考這篇文章

相關文章
相關標籤/搜索