TCP/IP 和 HTTP 詳解

目錄

  • TCP詳解
  • 長連接與短連接
  • HTTP的keep_alive和TCP的keep_alive的區別

TCP

定義:傳輸控制協議(TCP,Transmission Control Protocol)提供可靠端到端字節流的協議。TCP具備面對各種故障時的健壯性。

  • 可靠性;
  • 端到端;
  • 字節流

本節目錄

  • TCP報文頭格式要點詳解
  • 套接字Socket
  • TCP分包和組包
  • TCP連接(三次握手)
  • TCP釋放(四次揮手)
  • TCP與IP之間的關係
  • TCP與HTTP之間的關係

TCP報文頭格式要點

  • 起始部分是固定格式的20字節頭;
  • 源端口和目的端口標識了連接的本地端點。TCP端口(佔2字節)加上所在主機的IP地址(ipv4佔4字節)組成了48位的唯一斷點;
  • TCP流中的每一個數據字節都會被編號
  • 序列號seq:佔4個字節,用來標記數據段的中第一個字節的序號。由於TCP將要發送的所有數據字節進行分片後多次發送,爲了記錄多次發送的順序,就先將原本要發送的所有字節都編一個序號,第一個字節的序號由隨機生成;編號之後,分片後的每個報文段的第一個字節的序號就記錄在“序列號seq”中。比如說,原本要發50個字節,對所有字節按0-49編號後,如果按每個報文段8個字節分片,那麼會生成7個報文段,這7個報文段的序號seq分別記錄的是0、8、16、24、32、40、48;
  • 確認號ack:佔4個字節,接收方成功接收到數據後,會回覆一個ACK數據包,即接收到的上一次遠端主機傳來的seq然後+1,再發送給遠端主機,提示遠端主機已經成功接收上一次所有數據。發送方在一定時間內沒有收到服務端的ACK確認包後,就會重新發送TCP數據包。發送方收到了ACK,表明接收方已經接收到數據,保證了數據的可靠達到。
  • ACK標誌位是用來說明確認號ack的記錄是否是有效的,也就是說當ACK=1時,表明確認號ack是有效的,ACK=0時,確實號ack是可以被忽略的;
  • SYN標誌位被用於建立連接過程。當SYN=1,ACK=0時,表示這是一個連接請求報文段,若對方的響應報文段是SYN=1,ACK=1,那麼就表示對方同意連接。所以SYN=1表示該報文一個連接請求報文(此時ACK = 0),或者是一個對方的接受連接的響應報文(此時ACK = 1)。並且,SYN這個標誌位只有在TCP建立連接時纔會被置1,握手完成後SYN標誌位被置0
  • FIN標誌位用來釋放一個連接,當FIN=1時,表示此報文段的發送方的數據已經發送完畢,請求釋放連接。

TCP連接(三次握手)

三次握手其實指的就是,Client —> Server, Server —> Client,  Client —> Server 爲了確認連接關係的這三個報文。

上圖是用wireshark抓取的TCP三次握手的三個報文:

  • 客戶端ip是192.168.1.100,系統隨機分配的端口是57166;
  • 服務器ip是192.168.1.175,監聽的端口是9000;

第一次握手:57166 -> 9000 

建立連接,客戶端發送syn包(seq=x)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。

第一次握手是客戶端向服務器端發送握手報文,也稱爲[SYN]報文,從報文內容可以看出:

  • 第一次握手是[SYN]報文;
  • SYN =1 , ACK = 0;
  • 序列號Seq爲0,確認號Ack也爲0;

第二次握手:9000 -> 57166

服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(syn=y),即SYN+ACK包(seq=y ,ack =x+1),此時服務器進入SYN_RECV狀態;

第二次握手是服務器端對客戶端的確認報文,也稱[SYN, ACK]報文:

  • 第二次握手是[SYN, ACK]報文
  • SYN =1, ACK =1;
  • 序列號Seq爲0,Ack是在第一次握手的Seq=0基礎上+1,所以Ack=1;

第三次握手:57166 -> 9000

客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(seq=x+1, ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。

第三次握手是客戶端對服務器的??報文,也稱爲[ACK]報文:

  • 第三次握手是[ACK]報文;
  • SYN = 0, ACK = 1;
  • 序列號Seq在第一次握手的Seq=0基礎上+1,所以Seq=1,Ack在第二次握手Seq=0基礎上+1,所以Ack=1;

回顧三次握手:

上圖中取x=0,y=0,就和wireshark抓包的情況一致了。

TCP釋放(四次揮手)

  • 第一次揮手:客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。
  • 第二次揮手:服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
  • 客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。
  • 第三次揮手:服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
  • 第四次揮手:客戶端收到服務器的連接釋放報文後,必鬚髮出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
  • 服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。

TCP三次握手和四次揮手對比


作者:青柚_ 
來源:CSDN 
原文:https://blog.csdn.net/qq_38950316/article/details/81087809 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

【問題1】爲什麼連接的時候是三次握手,關閉的時候卻是四次握手?

答:因爲當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

【問題2】爲什麼不能用兩次握手進行連接?

答:3次握手完成兩個重要的功能,既要雙方做好發送數據的準備工作(雙方都知道彼此已準備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被髮送和確認。

       現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作爲例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S認爲連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已準備好,不知道S建立什麼樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認爲連接還未建立成功,將忽略S發來的任何數據分組,只等待連接確認應答分組。而S在發出的分組超時後,重複發送同樣的分組。這樣就形成了死鎖。

(對這個解答很滿意,當只做兩次握手時,也就意味着服務器迴應客戶端之後,認爲建立連接的過程就已經是OK了,準備發送具體的業務數據了,而真實情況是服務器的響應報文很有可能沒達到客戶端,就已經丟失了,此時客戶端就一直等待第二次握手的響應報文,而服務器端就已經覺得可以向客戶端發送業務數據了,並且還會等待客戶端對自己的迴應。這樣就導致客戶端還停留着構建連接的階段,等待服務端的第二次握手,對服務端的業務數據不感興趣,而服務端想客戶端發送業務數據,等待客戶端的響應,造成死鎖)

【問題3】如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?

TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75s發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。

【問題4】爲什麼TIME_WAIT狀態需要經過2個MSL(最大報文段生存時間)才能返回到CLOSE狀態?

答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最後的ACK回覆,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重複發送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK之後進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那麼Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP連接

TCP和IP的關係 

傳輸層和網絡層一樣,都提供面向連接和無連接兩種服務。兩者的面向連接都要經歷3個階段:連接建立、數據傳輸、連接釋放,另外,無連接的傳輸服務與無連接的網絡服務也極爲相似。那麼既然傳輸層和網絡層服務如此相似,那麼爲什麼要設立兩個獨立的層?事實上,傳輸層代碼完全運行在用戶的機器上,但是網絡層的代碼主要運行在有運營商操作的路由器上(至少對於廣域網是如此)。如果網絡層提供的服務不夠用,怎麼辦?如果它頻繁地丟失數據包該怎麼辦?如果路由器時常崩潰又該怎麼辦?

問題的答案在於,用戶對網絡層沒有真正的控制權,因爲他們不擁有路由器,所以不能用更好的路由器或者在數據鏈路層上用更好的錯誤機制來解決服務太差的問題。唯一的可能是在網絡層之上再加一層,由該層來提高網絡的服務質量。如果在一個無連接的網絡中,數據包被丟失或者發生錯位,則傳輸實體可以檢測到問題所在,並通過重傳來彌補這種錯誤。如果在一個面向連接的網絡中,傳輸實體在執行一個漫長傳輸任務期間,突然接到通知說它的網絡層連接已經被意外終止,而且也不知道當前正在傳輸的那些數據到底怎麼樣,那麼該傳輸實體可以與遠端的傳輸實體建立一個新的網絡層連接。利用新建立的連接,它可以向對等實體詢問哪些數據已經到達,哪些數據還沒有到達,然後從中斷的地方開始繼續向對方發送數據。本質上,由於傳輸層的存在,使得傳輸服務有可能比網絡服務更加可靠

傳輸層(TCP)與網絡層(IP)一起構成了網絡協議層次的核心,傳輸層架構在網絡層提供的服務之上,把數據傳遞服務從兩臺計算機之間擴展到了兩臺計算機上的進程之間

TCP傳輸實體接受本地進程的用戶數據流,將它們分割成不超過64KB(實際上去掉IP和TCP頭,通常不超過1460數據字節)的分段,每個分段以單獨的IP數據報形式發送。當包含TCP數據的數據報到達一臺機器時,它們被遞交給TCP傳輸實體,TCP傳輸實體重構出原始的字節流。

IP層幫助將TCP層將切碎的分片報文發送到目的地,但IP層並不保證數據報一定被正確的遞交到接收方,也不指示數據報的發送速度有多快。正是TCP負責既要足夠快地發送數據報,以便使用網絡容量,但又不能引起網絡阻塞;而且,TCP超時後,要重傳沒有遞交的數據報。即是被正確遞交的數據報,也可能存在錯序的問題,這也是TCP的責任,它必須把接收到的數據報重新裝配成正確的次序。

簡而言之,TCP是建立在IP的傳送功能之上,TCP必須提供可靠性的良好性能,這正是用戶所期望的而IP有沒有提供的功能


長連接與短連接

 

 


http的keep-alive和tcp的keepalive區別

HTTP Keep-Alive

在http早期,每個http請求都要求打開一個tpc socket連接,並且使用一次之後就斷開這個tcp連接。
使用keep-alive可以改善這種狀態,即在一次TCP連接中可以持續發送多份數據而不會斷開連接。通過使用keep-alive機制,可以減少tcp連接建立次數,也意味着可以減少TIME_WAIT狀態連接,以此提高性能和提高httpd服務器的吞吐率(更少的tcp連接意味着更少的系統內核調用,socket的accept()和close()調用)。
但是,keep-alive並不是免費的午餐,長時間的tcp連接容易導致系統資源無效佔用。配置不當的keep-alive,有時比重複利用連接帶來的損失還更大。所以,正確地設置keep-alive timeout時間非常重要。
keepalvie timeout
Httpd守護進程,一般都提供了keep-alive timeout時間設置參數。比如nginx的keepalive_timeout,和Apache的KeepAliveTimeout。這個keepalive_timout時間值意味着:一個http產生的tcp連接在傳送完最後一個響應後,還需要hold住keepalive_timeout秒後,纔開始關閉這個連接。
當httpd守護進程發送完一個響應後,理應馬上主動關閉相應的tcp連接,設置 keepalive_timeout後,httpd守護進程會想說:”再等等吧,看看瀏覽器還有沒有請求過來”,這一等,便是keepalive_timeout時間。如果守護進程在這個等待的時間裏,一直沒有收到瀏覽發過來http請求,則關閉這個http連接。

TCP KEEPALIVE

鏈接建立之後,如果應用程序或者上層協議一直不發送數據,或者隔很長時間才發送一次數據,當鏈接很久沒有數據報文傳輸時如何去確定對方還在線,到底是掉線了還是確實沒有數據傳輸,鏈接還需不需要保持,這種情況在TCP協議設計中是需要考慮到的。
TCP協議通過一種巧妙的方式去解決這個問題,當超過一段時間之後,TCP自動發送一個數據爲空的報文給對方,如果對方迴應了這個報文,說明對方還在線,鏈接可以繼續保持,如果對方沒有報文返回,並且重試了多次之後則認爲鏈接丟失,沒有必要保持鏈接。

http keep-alive與tcp keep-alive(重要!!!)

http keep-alive與tcp keep-alive,不是同一回事,意圖不一樣。http keep-alive是爲了讓tcp活得更久一點,以便在同一個連接上傳送多個http,提高socket的效率。而tcp keep-alive是TCP的一種檢測TCP連接狀況的保鮮機制。tcp keep-alive保鮮定時器,支持三個系統內核配置參數:

1 echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
2 echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
3 echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

keepalive是TCP保鮮定時器,當網絡兩端建立了TCP連接之後,閒置idle(雙方沒有任何數據流發送往來)了tcp_keepalive_time後,服務器內核就會嘗試向客戶端發送偵測包,來判斷TCP連接狀況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。如果沒有收到對方的回答(ack包),則會在 tcp_keepalive_intvl後再次嘗試發送偵測包,直到收到對方的ack,如果一直沒有收到對方的ack,一共會嘗試 tcp_keepalive_probes次,每次的間隔時間在這裏分別是15s, 30s, 45s, 60s, 75s。如果嘗試tcp_keepalive_probes,依然沒有收到對方的ack包,則會丟棄該TCP連接。TCP連接默認閒置時間是2小時,一般設置爲30分鐘足夠了。

也就是說,僅當nginx的keepalive_timeout值設置高於tcp_keepalive_time,並且距此tcp連接傳輸的最後一個http響應,經過了tcp_keepalive_time時間之後,操作系統纔會發送偵測包來決定是否要丟棄這個TCP連接。一般不會出現這種情況,除非你需要這樣做。

keep-alive與TIME_WAIT

使用http keep-alvie,可以減少服務端TIME_WAIT數量(因爲由服務端httpd守護進程主動關閉連接)。道理很簡單,相較而言,啓用keep-alive,建立的tcp連接更少了,自然要被關閉的tcp連接也相應更少了。

圖解http的keep-alive


參考文獻

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章