面試必問的http-1.1:三次握手-四次揮手

1:http爲什麼是不安全的?
    1:不能這麼問,
        首先http協議本身不存在安全問題,並且協議本身也幾乎不會成爲攻擊的對象。
        所謂的http不安全,
        是指應用http協議的服務器和客戶端,以及運行在服務器上的web應用資源容易受到攻擊,
        這些是不安全的。
        
    2:因爲http協議非常單純,它不具備會話管理(無狀態的),不具備加密處理。
        如果有需要,我們得重新開發,開發者需要自己設計(認證,會話管理)
        自行設計就有多樣性。
        
        那麼這些多樣新的設計,就可能存在了安全問題。
        
    3:使用的軟件(web軟件存在漏洞)

 

三次握手的過程

最開始的時候客戶端和服務器都是處於closed狀態。主動打開連接的爲客戶端,被動打開連接的是服務器。

1:TCP服務器進程先創建傳輸控制塊TCB,時刻準備接受客戶進程的連接請求,此時服務器就進入了LISTEN(監聽)狀態;

2:TCP客戶進程也是先創建傳輸控制塊TCB,然後向服務器發出連接請求報文,這是報文首部中的同部位SYN=1,同時選擇一個初始序列號 seq=x ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。

3:TCP服務器收到請求報文後,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=x+1,同時也要爲自己初始化一個序列號 seq=y,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。

4:TCP客戶進程收到確認後,還要向服務器給出確認。確認報文的ACK=1,ack=y+1,自己的序列號seq=x+1,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。

5:當服務器收到客戶端的確認後也進入ESTABLISHED狀態,此後雙方就可以開始通信了。

問題:

爲什麼TCP客戶端最後還要發送一次確認呢?

一句話,主要防止已經失效的連接請求報文突然又傳送到了服務器,從而產生錯誤。

如果使用的是兩次握手建立連接,假設有這樣一種場景,客戶端發送了第一個請求連接並且沒有丟失,只是因爲在網絡結點中滯留的時間太長了,由於TCP的客戶端遲遲沒有收到確認報文,以爲服務器沒有收到,此時重新向服務器發送這條報文,此後客戶端和服務器經過兩次握手完成連接,傳輸數據,然後關閉連接。此時此前滯留的那一次請求連接,網絡通暢了到達了服務器,這個報文本該是失效的,但是,兩次握手的機制將會讓客戶端和服務器再次建立連接,這將導致不必要的錯誤和資源的浪費。

如果採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文並且回覆了確認報文,但是客戶端不會再次發出確認。由於服務器收不到確認,就知道客戶端並沒有請求連接。

 

TCP連接的釋放(四次揮手)

數據傳輸完畢後,雙方都可釋放連接。最開始的時候,客戶端和服務器都是處於ESTABLISHED狀態,然後客戶端主動關閉,服務器被動關閉。

1:客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。

2:服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。

3:客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。

4:服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。

5:客戶端收到服務器的連接釋放報文後,必鬚髮出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。

6:服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。

 

1:爲什麼客戶端最後還要等待2MSL?

MSL(Maximum Segment Lifetime),TCP允許不同的實現可以設置不同的MSL值。

第一,保證客戶端發送的最後一個ACK報文能夠到達服務器,因爲這個ACK報文可能丟失,站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端還沒有給我回應,應該是我發送的請求斷開報文它沒有收到,於是服務器又會重新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接着給出迴應報文,並且會重啓2MSL計時器。

第二,防止類似與“三次握手”中提到了的“已經失效的連接請求報文段”出現在本連接中。客戶端發送完最後一個確認報文後,在這個2MSL時間中,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣新的連接中不會出現舊連接的請求報文。

 

2:爲什麼建立連接是三次握手,關閉連接確是四次揮手呢?

建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。

而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,而自己也未必全部數據都發送給對方了,所以己方可以立即關閉,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送,從而導致多了一次。

 

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

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

 

上面的描述可能難於理解:

這裏我搜集了網上一個將的比較好的,非常容易理解。

TCP/IP協議三次握手和四次揮手大白話解說

三次握手建立連接闡述:

第一次握手:客戶端要和服務端進行通信,首先要告知服務端一聲,遂發出一個SYN=1的連接請求信號,”服務端哥哥,我想給你說說話”。

第二次握手:當服務端接收到客戶端的連接請求,此時要給客戶端一個確認信息,”我知道了(ACK),我這邊已經準備好了,你現在能連嗎(SYN)”。

第三次握手:當客戶端收到了服務端的確認連接信息後,要禮貌的告知一下服務端,“好的,咱們開始聯通吧(ACK)”。

到此整個建立連接的過程已經結束,接下來就是雙方你一句我一句甚至同時交流傳遞信息的過程了。

四次揮手斷開連接闡述:

第一次揮手:雙方交流的差不多了,此時客戶端也已經結尾了,接下來要斷開通信連接,所以告訴服務端“我說完了(FIN)”,此時自身形成等待結束連接的狀態。

第二次揮手:服務端知道客戶端已經沒話說了,服務端此時還有兩句心裏話要給客戶端說,“我知道你說完了(ACK),我再給你說兩句,&*……%¥”。

第三次揮手:此時客戶端洗耳恭聽繼續處於等待結束的狀態,服務器端也說完了,自身此時處於等待關閉連接的狀態,並對告訴客戶端,“我說完了,咱們斷了吧(FIN)”。

第四次揮手:客戶端收知道服務端也說完了,也要告訴服務端一聲(ACK),因爲連接和斷開要雙方都按下關閉操作才能斷開,客戶端同時又爲自己定義一個定時器,因爲不知道剛纔說的這句話能不能準確到達服務端(網絡不穩定或者其他因素引起的網絡原因),默認時間定爲兩個通信的最大時間之和,超出這個時間就默認服務器端已經接收到了自己的確認信息,此時客戶端就關閉自身連接,服務器端一旦接收到客戶端發來的確定通知就立刻關閉服務器端的連接。

到此爲止雙方整個通信過程就此終結。

 

這裏要聲明一下:斷開鏈接不一定就是客戶端,誰都可以先發起斷開指令,另外客戶端和服務端是沒有固定標準的,誰先發起請求誰就是客戶端。

問題:

1.爲什麼斷開鏈接的時候客戶端設置的定時器時間等待要2MSL(兩個通信報文的最大時間)?

這個問題也很好理解,當客戶端最終告訴服務器端斷開確認的時候,他不知道自己的發出的指令是否能準確的一次性被服務器接收。

假如服務器沒有接收到(這已經耗費了一個報文的最大通信時間了),服務器端將會重新發起一個結束通話的指令(FIN)到客戶端,客戶端又接收到了服務器發來的結束通信指令將繼續給服務器進行一個確認,有人會說那要是客戶端發出的確認信息服務端沒收到,而服務端重發的斷開指令客戶端也沒收到怎麼辦,說實話我也無奈,遇到這種情況咱們乾脆認爲網確實不行了。

 

 

:

心跳機制: 

心跳機制的原理很簡單:

客戶端每隔N秒向服務端發送一個心跳消息,服務端收到心跳消息後,回覆同樣的心跳消息給客戶端。

如果服務端或客戶端在M秒(M>N)內都沒有收到包括心跳消息在內的任何消息,即心跳超時,

我們就認爲目標TCP連接已經斷開了。

整理了一個網上的博客說明心跳機制:

https://blog.csdn.net/bjrxyz/article/details/71076442

1:我面試時經常會問的一個問題是當TCP兩端A、B建立了連接後,如果一端拔掉網線或者拔掉電源,那麼另一端能夠收到通知嗎? 

答案是不會,但是隻有少數人能夠正確的回答這個問題。

 

原因

TCP是一種有連接的協議,但是這個連接並不是指有一條實際的電路,而是一種虛擬的電路。TCP的建立連接和斷開連接都是通過發送數據實現的,也就是我們常說的三次握手、四次揮手。TCP兩端保存了一種數據的狀態,就代表這種連接,TCP兩端之間的路由設備只是將數據轉發到目的地,並不知道這些數據實際代表了什麼含義,也並沒有在其中保存任何的狀態信息,也就是說中間的路由設備沒有什麼連接的概念,只是將數據轉發到目的地,只有數據的發送者和接受者兩端真正的知道傳輸的數據代表着一條連接。

 

但是這就說明了一點,如果不發送數據那麼是無法斷開連接的。正常情況下當TCP的一端A調用了SOCKET的close或者進程結束,操作系統就會按照TCP協議發送FIN數據報文。B端收到後就會斷開連接。但是當出現了上文所說的異常情況時:被拔掉網線或者斷掉電源,總結起來就是沒有機會發出斷開的FIN數據報文。那麼和A直連的路由設備雖然知道A設備已經斷開了,但是路由設備並沒有保存連接的狀態信息,所以路由設備也就不可能去通知B端A端的斷開。而B端沒有收到斷開的數據報文就會依然保持連接。所以A端拔掉網線或者斷掉電源後B端是沒辦法收到斷開連接的通知的。

 

解決方案

保持連接並不是毫無代價的,如果這種異常斷開的連接有很多,那麼勢必會耗費大量的資源,必須要想辦法檢測出這種異常連接。

檢測的方法很簡單,只要讓B端主動通過這個連接向A端繼續發送數據即可。上文說過,A端異常斷開後,和A端直接連接的路由器是知道的。當B端發送的數據經過轉發後到達這個路由器後,必然最終會返回B端一個目的不可達。此時B端立刻就會知道這條連接其實已經異常斷開了。

但是B端不可能知道什麼時候會出現這種異常,所以B端必須定時發送數據來檢測連接是否異常斷開。數據的內容無關緊要,任何數據都能達到這個效果。這個數據就是我們經常在TCP編程中所說的心跳。

 

KEEP_ALIVE

TCP協議本身就提供了一種這樣的機制來探測對端的存活。TCP協議有一個KEEP_LIVE開關,只要打開這個開關就會定時發送一些數據長度爲零的探測心跳包,發送的頻率和次數都可以設置,具體的方法在網上搜索tcp keepalive即可,網上有很多文章,這裏不再贅述。

 

雙向心跳

那麼是否只是一端向另一端發送心跳就行了呢?顯然不行。因爲兩端都有可能發生異常斷開的情況。所以TCP連接的兩端必須都向對端發送心跳。

 

總結

TCP中不使用心跳通常來說並沒有什麼問題,但是一旦遇到了連接異常斷開,那麼就會出現問題。所以任何一個完善的TCP應用都應該使用心跳。

心跳的意義對於很多TCP的初學者而言是個大坑,我寫這篇文章希望初學者能夠在編寫TCP程序時避免這個坑,同時也希望面試者能夠深入理解TCP的心跳機制,能夠取得更好的面試結果。

 

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