前言
不管是客戶端還是服務端開發,網絡協議這一塊都是要學習和了解的
工作和面試中,網絡協議都會用到,雖然學習網絡對編碼沒有實質的幫助,但是對你處理網絡連接問題,幫助很大。下面來學習一下;
緣起
在世界上各地,各種各樣的電腦,運行着各自不同的操作系統為大家服務,這些電腦在表達同一種信息的時候,所使用的方法是千差萬別的。
計算機使用者意識到,計算機知識單兵作戰,並不會發揮太大的作用,只有把他們聯合起來,電腦才會發揮它最大的潛力。
於是人們就想發設發的,用電線把電腦連接到一起。但是簡單的連接一起是遠遠不夠的,就好像語言不通的兩個人互相見了面,完全不能交流信息。因為他們需要定義一些共通的東西來進行交流,TCP/IP就誕生 了
TCP/IP不是一個協議,而是一個協議族的統稱。里面包括了IP協議,IMCP協議,TCP協議,以及我們更加熟悉的http,ftp協議等等。電腦有了這些,就好像學會了外語一樣,就可以和其他的計算機終端做自有的交流了。
什么是TCP
TCP(傳輸控制協議)是一種面向連接的,可靠的,基於字節流的傳輸層通信協議,它完成第四層傳輸層所指定的功能,網絡模型下面介紹。
TCP協議的特點是:
1,面向連接:一定【一對一】才能連接,不能像UDP協議可以一個主機同時向多個主機發送消息,也就是一對多是無法做到的。
2,可靠交付:無論網絡鏈路中出現了怎樣的鏈路變化,TCP都可以保證一個報文一定能夠到達接收端。
3,面向字節流:也就是說僅僅把上層協議傳遞過來的數據當成字節傳輸。
網絡模型
七層模型
國際標准化組織ISO,在1981年正式推薦了一個網絡系統結構一七層參考模型,也叫開放系統互聯模。由於這個標准模型的建立,使得各種計算機網絡均向它靠攏,大大推動了網絡通信的發展。
這個ISO層網絡模型各層的名字,主要功能對應的典型設備和傳輸單位如下圖:
這個七層網絡模型在數據的傳輸過程中還會對數據進行封裝,如下圖:
ISO 層網絡模型中,當一台主需要傳送用戶數據data時,數據首先通過應用層的接口進入應用層。
先看幾個常見報頭屬於簡寫:
1,應用層報頭:Ppplication Header 簡稱 AH
2,表示層報頭:Presentation Header 簡稱 PH
3,會話層報頭:Session Header 簡稱: SH
4,傳輸層報頭:Transport Header 簡稱 TH。
5,網絡層報頭:NetWork Header 簡稱:NH
6,數據鏈路層報頭:Data link Header 簡稱 DH
7,應用層協議數據單元:Protocol Data Unit 簡稱 PDU
8,數據鏈路層報尾:Data link Terimination 簡稱 DT.
在應用層,用戶的數據被加上應用層的報頭AH,形成應用層協議數據單元PDU,然后被遞交到下層表示層。
表示層並不關心上層應用層的數據格式,而是把整個應用層遞交的數據包,看成一個整體進行封裝,即佳航表示層的報頭PH,然后,遞交到下層會話層。
同樣,會話層,傳輸層,網絡層(假設用TCP傳輸,則是TCP數據+IP包頭)、數據鏈路層(把上層的TCP數據+IP頭同意稱為幀數據,即幀+幀數據+幀尾(CRC)也都要分別給上層遞交下來的數據加上自己的報頭)
他們是:會話層報頭SH,傳輸層報頭TH,網絡層報頭NH和數據鏈路層報頭DH。其中,數據鏈路層還要給網絡層遞交的數據加上數據鏈路層報尾形成最終的一幀數據。
當一幀數據,通過物理層傳送到目標主機的物理層時,該主機的物理層把它遞交到上層------數據鏈路層。數據鏈路層負責去掉數據幀的幀頭部和尾部,如果數據沒有出錯,則遞交到上層網絡層。
同樣,網絡層、傳輸層、會話層、表示層、應用層、也要做類似的工作,最終,原始數據被遞交到目標主機的具體應用程序中。
五層網絡模型
五層模型的網絡體系也經常被提到,這五層的名字和功能分表如下所述:
1,應用層: 確定進程之間通行的性質,以滿足用戶的需求。應用層協議有很多。如支持萬維網應用的HTTP協議,支持電子郵件的SMTP協議等等
2,傳輸層:負責主機間不同進程的通信。這一層的協議有面向連接的TCP(傳輸控制協議),無連接的UDP(用戶數據報協議);數據傳輸的單位稱為報文段或者用戶數據報。
3,網絡層:負責分組交換網中不同主機間的通信。作用為:發送數據時,將運輸層中的報文段或者用戶數據報封裝成IP數據報,並選擇合適路由。
4,數據鏈路層:負責將網絡層的IP數據報組裝成幀。
5,物理層:頭明明的傳輸比特流。
四層網絡模型
前面的兩種模型都是學術上的概念,使用並不廣泛,還有一個四層模型,使用最為廣泛 ----TCP/IP分層模型。幾種模型如下圖:
TCP/IP分層的四層模型的協議層分別完成一下的功能:
1,網絡接口層:包括用戶協作IP數據,在已有網絡截止上傳輸的協議。實際上TCP/IP標准並不定義與ISO數據鏈路層和物理層相對應的功能。相反,它定義了像ARP(地址解析協議)這樣的協議,提供了TCP/IP協議的數據結構和實際物理硬件之間的接口。
2,網絡層:網絡層對應於IOS七層參考模型的網絡層。本層包含IP協議,RIP協議(路由信息協議),負責數據的包裝,尋址和路由。同時還包含ICMP(網間控制報文協議)用來提供網絡診斷信息。
3,傳輸層:傳輸層對應於IOS七層考考模型的傳輸層,它提供兩種端到端的通信服務。其中TCP協議提供可靠的數據流運輸服務,UDP協議提供不可靠的用戶數據報服務。
4,應用層:應用層對應於IOS七層參考模型的應用層和表示層。因特網的應用層協議包括FTP(文件傳輸協議),HTTP(超文本傳輸協議),Telent(遠程終端協議),SMTP(簡單郵件傳送協議),IRC(因特網終端會話),NNTP(網絡新聞傳輸協議)等
綜上所述,我們需要知道TCP協議在網絡的IOS的七層模型的第四層傳輸層,IP協議在第三層網絡層,ARP協議在第二層的數據鏈路層;在第二層上的數據叫Frame,在第三層上的數據叫Packet,第四層的數據叫Segment所有程序的數據首先會打包到TCP的Segment中。
然后TCP的Segment會打包到IP的Packet,然后在打包到以太網Ehternet的Frame中,傳到端后,各自解析自己的協議,然后把數據交給更高層的協議處理。
TCP頭格式
在學習TCP連接之前,還要學習一下TCP頭部格式。因為TCP連接建立,需要用到TCP包來交換和管理數據,下面看一下TCP頭部格式。
TCP頭部里每個字段都為管理TCP連接和控制數據流起了重要作用。
16位端口號:告知主機該報文段是來自哪里(源端口)以及傳給哪個上層協議或者應用程序(目的端口)的
進行TCP通信時,客戶端通常使用系統自動選擇的臨時端口號,而服務器則使用知名服務端口號。所有知名服務使用的端口號都定義在/etc/services文件中
32位序號(sequence number):一次TCP通信(從TCP連接建立到斷開)過程中傳輸方向上的字節流的每個字節編號。
32位確認號(acknowledgement number):用作對另一方發送來的TCP報文段的相應其值是收到的TCP報文段的序號值加1.
4位頭部長度(header length):標識該TCP頭部有多少個32bit(4byte 因為最大能表示15,所以TCP 頭部最長是60 byte)
6位標志位包含如下幾項:
·URG 標志,表示緊急指針是否有效。
·ACK標志,表示確認號是否有效,一般稱攜帶ACK標志的TCP報文段為“確認報文段”
·PSH標志,提示接收端應用程序應該立即從TCP接收緩沖區中讀走數據,為接收后續數據騰出空間(如果應用程序不降接收到數據讀走,他們就會停留在TCP接收緩沖區中)
·RST標志,表示要求地方重新建立連接,一般稱攜帶RST標志的TCP報文段為“復位報文段”
·SYN標志,表示請求建立連接,一般稱攜帶SYN標志的TCP報文段為“同步報文段”。FIN標志,表示通知對方本端要關機連接了,一般稱攜帶FIN標志的TCP報文段為“結束報文段”
16位窗口大小(window size):是TCP流量控制的一個手段。這里說的窗口,指的是接收通告窗口。它告訴對方本端的TCP接收緩沖區還能容納多少字節的數據,這樣對方就可以控制發送數據的速度。
16位校驗和(TCP checksum):有發送端填充,接收端對TCP報文段執行CRC算法,以檢驗TCP報文段在傳輸過程中是否有損壞。注意,這個校驗不進包括TCP頭部,也包括數據部分。這也是TCP可靠傳輸的一個重要保障。
16位緊急指針(urgent pointer):是一個正的偏移量。它和序號字段的值相加表示最后一個緊急數據的下一個字節的序號。確切的說,這個字段是緊急指針相對當前序號的偏移,不妨稱之為“緊急偏移”。TCP的緊急指針是發送端向接收端發送緊急數據的方法。
綜上,你需要注意如下幾點:
TCP的包沒有IP地址的,那是IP層上的事,但是有源端口和目的端口。
一個TCP鏈接需要四元組(src_ip,src_port ,dst_ip,dst_port)來表示是同一個連接,准備說是五元組,還有一個是協議,但是因為這里只是強調TCP協議,搜易只說四元組
Sequence Number 是包的序號,用來解決網絡報的亂序問題。
Acknowledgement Number 就是ACK,用戶確認收到,永愛解決不丟包的問題。
Window Advertised Window 也就著名的滑動窗口,用來解決流量控制問題。
TCP Flag 也就是包的類型,主要是用於操控TCP的狀態機的。
TCP三次握手
其實,網絡上的傳輸是沒有連接的,TCP是一樣的TCP所謂的“連接”,其實只不過是在通信的雙方維護一個“連接狀態”,讓它看上去好像有連接一樣。所以,TCP的狀態變換是非常重要的。
先來看一下著名的三次握手圖
TCP連接的建立可以簡單的稱為三次握手,而連接的中止則可以稱為四次握手。
建立連接TCP/IP協議中,TCP協議提供可靠的鏈接服務,采用三次握手建立一個鏈接。
1,第一次握手:建立連接時,客戶端發送SYN包到服務器,並進入SYN_SEND狀態,等待服務器確認。
2,第二次握手:服務器接收到SYN包,必須確認客戶的SYN,同時自己也發送一個SYN包,即SYN+ACK包,此時服務器進入SYN_RECV狀態。
3,第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK,此包發送完畢,客戶端和服務器進入ESTABLISHE狀態。
完成三次握手,客戶端和服務器開始傳送數據,也就是ESTABLISHED狀態。
連接建立中的異常
建連接時SYN超時問題
如果server端因為某種情況沒有收到client回來的ACK,那么,這個連接還處於一個未建立的狀態。於是,server端如果在一定時間內沒有收到,則server端的TCP會重發SYN_ACK。
在linux下,默認重試次數是5次,重試的間隔時間從1s開始每次都翻倍,5次的重試時間間隔為1s,2s,4s,8s,16s,總共31s,第5此發出后還要等32s都知道第5此也超時了,如果第五次重傳之后,還未收到客戶端的ACK,server端的TCP才會把斷開這個連接。
關於SYN Flood 攻擊
攻擊者短時間偽造不同 IP 地址的 SYN 報文,服務端每接收到一個 SYN 報文,就進入SYN_RCVD 狀態,但服務端發送出去的 ACK + SYN 報文,無法得到未知 IP 主機的 ACK 應答,久而久之就會占滿服務端的 SYN 接收隊列(未連接隊列),使得服務器不能為正常用戶服務。
避免方式
設置 tcp_syncookies = 1。當 SYN 隊列滿了后,TCP 會通過源地址端口、目標地址端口和時間戳打造出一個特別的 Sequence Number 發回去(又叫cookie)。
如果是攻擊者則不會有響應,如果是正常連接,則會把這個 SYN Cookie 發回來,然后服務端可以通過 cookie 建連接。
設置 netdev_max_backlog 的值,確定鏈接隊列的大小。當網卡接收數據包的速度大於內核處理的速度時,會有一個隊列保存這些數據包。
通過設置 netdev_max_backlog 的值,確定 SYN_RCVD 狀態連接的最大個數。
通過設置 tcp_abort_on_overflow 的值。當超出處理能時,對新的 SYN 直接回報 RST,丟棄連接。
TCP四次握手
TCP的鏈接斷開
TCP 一個特別的概念叫做半關閉,這個概念是說,TCP的鏈接是全雙工(可以同時發送和接收)鏈接,因此在關閉連接的時候,必須關閉傳和送兩個方向上的鏈接。
客戶端給服務端FIN的TCP報文,然后服務器返回給客戶端一個確認的ACK報文,並且發送一個FIN報文,當客戶機回復ACK報文后,連接就結束了。
在簡歷連接的時候,通信的雙方要互相確認對方的最大報文長度(MSS),以便通信。
一般這個SYN長度是MTU減去固定IP的首部和TCP首都長度。對於一個以太網,一般可以達到1460byte。當然如果對於非本地的IP,這個MSS可能就只有536byte,而且如果中間的傳輸網絡的MSS更加的小的話,這個值還會變得更小。
為什么建連接要三次握手,而斷開連接需要四次握手
對於建連接的三次握手,主要是要初始化 Sequence Number 的初始值。通信的雙方要互相通知對方自己的初始化的 Sequence Numbe,所以叫 SYN 。
這個號要作為以后的數據通信的序號,以保證應用層接收到的數據不會因為網絡上的傳輸問題而亂序( TCP 會用這個序號來拼接數據)。
對於四次揮手,其實仔細看則是兩次,因為 TCP 是全雙工的,所以,發送方和接收方都需要 FIN 和 ACK。
只不過,有一方是被動的,所以看上去就成了所謂的四次揮手 。如果兩邊同時斷連接,那就會就進入到 CLOSING 狀態,接着就是TIME_WAIT 狀態。
斷開連接中的異常
TIME_WAIT數量太多
從上面的描述可以知道,TIME_WAIT 是個很重要的狀態,但是如果在大並發的短鏈接下,TIME_WAIT 就會太多。TIME_WAIT過多會占用大量的內存資源和端口資源。
優化法一:tcp_tw_reuse
設置tcp_tw_reuse = 1,則可以復用處於 TIME_WAIT 的 socket 為新的連接所用。
有一點需要注意的是,tcp_tw_reuse 功能只能用客戶端(連接發起方),因為開啟了該功能,在調用 connect() 函數時,內核會隨機找一個 time_wait 狀態超過 1 秒的連接給新的連接復用。
使用 tcp_timestamps = 1 選項,還有一個前提,需要打開對 TCP 時間戳的支持,即這個時間戳的字段是在 TCP 頭部的「選項」里,用於記錄 TCP 發送方的當前時間戳和從對端接收到的最新時間戳。
由於引入了時間戳,我們在前面提到的 2MSL 問題就不復存在了,因為重復的數據包會因為時間戳過期被自然丟棄。
優化法二:tcp_max_tw_buckets
這個值默認為 18000,當系統中處於 TIME_WAIT 的連接一旦超過這個值時,系統就會將后面的 TIME_WAIT 連接狀態重置。
這個方法過於暴力,而且治標不治本,帶來的問題遠比解決的問題多,不推薦使用。
TCP狀態流轉
接下來再看一下著名的 TCP 狀態流轉圖。
CLOSED狀態:表示初始狀態。
LISTEN狀態:表示服務器端的某個 socket 處於監聽狀態,可以接受連接。
SYN_SENT狀態:在服務端監聽后,客戶端 socket 執行 CONNECT 連接時,客戶端發送 SYN 報文,此時客戶端就進入 SYN_SENT 狀態,等待服務端的確認。
SYN_RCVD狀態:表示服務端接收到了SYN 報文,在正常情況下,這個狀態是服務器端的 socket 在建立 TCP 連接時的三次握手會話過程中的一個中間狀態,很短暫,基本上用網絡查詢工具 netstat 是很難看到這種狀態的。因此這種狀態時,當收到客戶端的 ACK 報文后,它會進入到 ESTABLISHED 狀態。
ESTABLISHED狀態:表示連接已經建立了。
FIN_WAIT_1狀態:這個是已經建立連接之后,其中一方請求終止連接,等待對方的 FIN 報文 。
FIN_WAIT_1 狀態是當 socket 在 ESTABLISHED 狀態時,它想主動關閉連接,向對方發送了 FIN 報文,此時該 socket 即進入到 FIN_WAIT_1 狀態。而當對方回應 ACK 報文后,則進入到 FIN_WAIT_2 狀態。
當然在實際的正常情況下,無論對方處於何種情況,都應該馬上回應 ACK 報文,所以 FIN_WAIT_1 狀態一般是比較難見到的,而 FIN_WAIT_2 狀態還可以用 netstat 看到。
FIN_WAIT_2狀態:實際上 FIN_WAIT_2 狀態下的 socket ,表示半連接,即有一方要求關閉連接,但另外還告訴對方:我暫時還有點數據需要傳送給你,請稍后再關閉連接。
TIME_ WAIT狀態:表示收到了對方的 FIN 報文,並發送出了 ACK 報文,就等 2MSL 后即可回到 CLOSED 可用狀態了。如果在 FIN_WAIT_1 狀態下,收到了對方同時帶 FIN 標志和 ACK 標志的報文時,可以直接進入到 TIME_WAIT 狀態,而無需經過 FIN_WAIT_2 狀態。
CLOSING狀態:這種狀態比較特殊,實際情況中應該是很少見。正常情況下,當發送 FIN 報文后,按理來說是應該先收到(或同時收到)對方的ACK 報文,再收到對方的 FIN 報文 。但是 CLOSING 狀態表示你發送 FIN 報文后,並沒有收到對方的 ACK 報文,反而收到了對方的 FIN 報文 。
如果雙方幾乎在同時關閉一個 socket 的話,那么就出現了雙方同時發送 FIN 報文的情況,就會出現 CLOSING 狀態,表示雙方都正在關閉 socket 連接。
CLOSE_WAIT狀態:表示在等待關閉。當對方關閉一個 socket 后發送 FIN 報文給自己時,系統將毫無疑問地會回應 ACK 報文給對方,此時則進入到 CLOSE_WAIT 狀態。
接下來呢,實際上你真正需要考慮的事情是察看你是否還有數據發送給對方,如果沒有,那么你也就可以關閉這個socket了,發送 FIN 報文給對方,即關閉連接 。CLOSE _WAIT 狀態下,需要完成的事情是等待你去關閉連接。
LAST_ACK狀態:這個狀態還是比較好理解的,它是被動關閉 方在發送 FIN 報文后,最后等待對方的 ACK 報文。
CLOSED狀態:當收到 ACK 報文后,也即可以進入到 CLOSED 可用狀態了。
2MSL 等待狀態:在 FIN_WAIT_2 發送了最后一個 ACK 數據報以后,要進入 TIME_WAIT 態,這個狀態是防止最后一次握手的數據報沒有傳送到對方那里而准備的。
由於 socket 2MSL 狀態,使得應用程序在 2MSL 時間內無法再次使用同一個 socket ,對於客戶程序還好 些,但是對於服務程序(httpd),它總是要使用同一個端口來進行服務,而在 2MSL 時間內,啟動 httpd 就會出現錯誤(插口被使用)。
為了避免這個錯誤,服務器給出了一個平靜時間的概念,這是說在 2MSL的時間內,雖然可以重新啟動服務器,但是這個服務器還是要平靜地等待 2MSL 的時間才能進行下一次連接。
FIN WAIT_2 狀態:這就是著名的半關閉狀態了,這是在關閉連接時,客戶端和服務器兩次握手之后的狀態 。
在這個狀態下,應用程序還有接收數據的能力。已經無法發送數據,但是也有一種可能是,客戶端處於FIN_WAIT_2 狀態,而服務器則一直處於 WAIT_CLOSE 狀態,直到應用層來決定關閉這個狀態。
RST 同時打開和同時關閉:RST 是另一種關閉連接的方式,應用程序應該可以判斷RST 包的真實性,即是否為異常中止 而同時打開和同時關閉則是兩種特殊的 TCP 狀態,發生的概率很小。
總結
本文主要講述了網絡分層模型,以及各層的作用,數據包是怎么組裝和拆包的。TCP 包結構也大致學習了下,還有 TCP 連接的建立和斷開。
TCP 連接建立之后才開始發數據包,所以 TCP 三次握手很重要。TCP 三次握手中也可能存在一些異常,只有徹底搞懂三次握手才能正確處理這些異常。
TCP 四次揮手也很重要,server 中經常要接受和斷開連接。對應斷開連接中的異常,以及服務器請求量過多,只有在搞懂 TCP 四次揮手以后,處理這些問題才能得心應手。