標籤:

為什麼http請求要3次握手與4次揮手?

一朋友去面試時,面試官問到這樣一問題?當時咱們都覺得面試官有意刁難,尼瑪現在覺得自己好膚淺。。。


因為HTTP是一個基於TCP的協議,而TCP是一種可靠的傳輸層協議.

建立TCP連接時會發生:三次握手(three-way handshake)

firefox &> nginx [SYN] 在么

nginx &> firefox [SYN, ACK] 在

firefox &> nginx [ACK] 知道了

關閉TCP連接時會發生:四次揮手(four-way handshake)

firefox &> nginx [FIN] 我要關閉連接了

nginx &> firefox [ACK] 知道了,等我發完包先

nginx &> firefox [FIN] 我也關閉連接了

firefox &> nginx [ACK] 好的,知道了

幾個報文的標識的解釋:

SYN: synchronization(同步)

ACK: acknowledgement(確認:告知已收到)

FIN: finish(結束)

在HTTP/1.1中,keep-alive能夠復用TCP連接,減少TCP三次握手的次數,從而提升性能.

結合到PHP編程中,拿Swoole引擎內置的非同步HTTP伺服器來說說:

調用 $res-&>end() 將結束HTTP請求,但不會關閉HTTP連接,因為Swoole支持keep-alive.

調用 $serv-&>close($res-&>fd) 將關閉HTTP連接.

這種ack確認機制在應用邏輯開發上也是很有用的,比如你用PHP+Swoole開發一個即時通訊軟體,要確保不丟消息,要做到:

A &<=&> Server &<=&> B

A發送消息給B後,如果A在指定時間內沒有收到Server的消息通知,那麼A就要超時重發. 正常情況下,Server收到A的消息後推送給B,B收到消息後通知Server,Server再通知A. 當然,A的超時重發可能會導致B收到重複的消息,所以B接受消息時要進行去重.

如果要考察HTTP,我覺得問下HTTP請求/響應報文的組成可能會更好.

HTTP請求報文組成:請求行+請求頭+請求體

HTTP響應報文組成:響應行+響應頭+響應體

請求行: 請求方法(HEAD/GET/POST) + 請求URL + HTTP協議版本

響應行: HTTP協議版本 + 狀態碼 + 狀態碼描述

請求頭: 比如客戶端的Cookie和User-Agent就放在這裡.

響應頭: 比如伺服器的Set-Cookie和Server信息就放在這裡.

請求體: 比如客戶端POST的數據就放在這裡(對比:GET的數據放在請求行的URL里).

響應體: 比如伺服器返回的HTML和JSON數據就放在這裡.

curl -I http://mysite

curl這樣發起的HTTP請求的請求方法(request_method)就是HEAD,可以這樣查看:

PHP-FPM: header("請求方法: $_SERVER["REQUEST_METHOD"]");

Swoole: $res-&>header("請求方法", $req-&>server["request_method"]);


要麼你記錯了,要麼你的面試官是個假的

3次握手,4次揮手是針對TCP連接來說的,HTTP協議本身不關注這塊

3次握手,用來保障通訊雙方有通信的基礎

4次揮手,用來保障通訊雙方可以安全的回收TCP通信的系統資源


假設2個人要相互確認對方已經收到自己的消息需要幾步:

server: 嘿你在嗎?

you: hei 我在,

server : 好的我發消息了,

server----&>you

server: 嘿我發完了,

you: 好的,我收完了

server: 好的,我關連接了.

you:好的.我也關連接了


HTTP請求 不一定要 3次握手與4次揮手 ……

HTTP協議 又不一定得用 TCP/IP協議……

——————————————————————

面試官 要麼就是想 看看是否對基礎知識有了解

要麼就是想以此引出 TCP/IP相關方面的問題

然而問的確實不專業……


哈哈,我也要反對一下那個高票,上了半年班,發現了一個問題,有的人處理不了的問題,就會找出各種各樣的理由,而且你還沒法反駁,這樣就回到了郭德綱說過的那個問題,有些人你跟他談藝術,他跟你講道德,你跟他講道德,他又會給你講別的,


反對目前最高票的答案,這個答案明顯給人一種吃不到葡萄就說葡萄酸的意味,好比高考出了一道數學題,沒有做出來,就說這道題沒有意義,balabala。

回到正題,面試官要麼是說錯了,要麼就是意指tcp/ip,有答案已經說明了三次和四次的作用了,如果真的理解了三次握手和四次揮手,對你理解網路編程是非常有指導意味的,例如java中的bind,listen,accept等等,還有tcp的狀態字如backlog等,學習到了這一步,就可以理解一些基本的io模型的問題,當然前提是要能理解為什麼三次揮手和四次握手要這麼做,學習的深入再去看netty這些簡直太爽了。第一次看到有人(高票答主)竟然有學這個沒有意義的想法,簡直不能理解。


因為現在的http協議用的是tcp作為傳輸層。倘若大家的能力非常強,可以自己寫一個協議叫做MMp也是可以的,你還可以設置根本不需要發什麼ack/syn握手握手去。

然而為什麼要發送ack/syn傳來傳去呢?

比較白話的說法如下:

韓梅梅和李雷吵架了,李雷之後並沒有找韓梅梅。這時候韓梅梅就想確認李雷死了沒有。

韓梅梅首先給李雷發信息問:死鬼,你死了嗎?

李雷這時候可能在打dota,lol,王者,不能及時回復,那韓梅梅就著急會重複發:你死了嗎!你沒死吧!你真的死了嗎?

所以,這就是tcp的第一次握手,就是要確認別人是否存在能響應。

那李雷畢竟不能辜負韓梅梅,他回復了:我沒死。

然而李雷其實面臨著一個尷尬的境地,他回復了韓梅梅,但是確不知道韓梅梅是否還在生氣,或者說韓梅梅是否還在手機旁邊,所以他現在也只能幹等韓梅梅回復或者重複發信息給她(現實其實是他跑去繼續玩遊戲)。

這就是第二次握手了,起碼在這一步知道了對方肯定是存在的了,但是想要保持連接下去,你得看看對方是否「可以傳輸」

韓梅梅收到了李雷說:我沒死的消息,那她基本就可以確定李雷沒死了,這時候已經不需要確認對方是否沒死了,那韓梅梅現在就可以告訴李雷一個驚天的消息:今晚我們去酒店吧!

如果李雷接受到了,我們今晚去酒店吧,那就基本上可以確認雙方都不生氣了,並且活著,而且還可以去開房了……


對應到現實中的tcp:

  1. 第一個握手包韓梅梅發送syc包給李雷,如果李雷沒及時回復,那韓梅梅會一直猛發
  2. 第二個握手包就是李雷收到韓梅梅的syc後,回發syc/ack給韓梅梅,如果韓梅梅沒有回復,那李雷也會猛發
  3. 第三個握手包就是韓梅梅知道李雷沒死,發給他一個ack,然後韓梅梅單方面就變成了「Established」狀態,如果李雷收到了,就也會變成「Established」狀態;如果沒有那就一直是「active」狀態

少於三次握手都不能確定雙方是否連接並且可以相互發消息;多於三次,就是浪費了..


如果原話是這樣,是有毛病可挑的。

HTTP本身是應用層協議,協議本身並不約束傳輸層用的啥。

HTTP 1.1 RFC 2616 也提到

「HTTP communication usually takes place over TCP/IP connections. The

default port is TCP 80 [19], but other ports can be used. This does

not preclude HTTP from being implemented on top of any other protocol

on the Internet, or on other networks. 」

不過現有 HTTP 協議實現的確是長在 TCP/IP 上的。所以才有數據傳輸層的三次握手。

如果傳輸層並不是 TCP/IP ,那就不一定有三次握手什麼的。

所以……你是可以這麼噴回去的……


講一下四次揮手

伺服器說:老哥,我要掛了哈,沒有東西要發了,

客戶端說:好的我知道了

此時伺服器開始準備掛的狀態,因為他等待也許客戶端還有話說

客戶端又說:我也沒東西了,你掛吧

伺服器說:好的,我知道了

然後斷開連接

其實很多東西都是人的社交演變,網路也是這樣


我猜他是故意這麼問的,其實他是間接問你HTTP協議底層是用什麼協議實現可靠傳輸以及它的特點。

另外,我還被問過類似的問題:在帶寬足夠的情況下,為什麼http下載速度是1 2 4 8……這樣的增長規律,而不是直接以最大速度下載。其實他是在考察tcp的擁塞控制。直白問你三次握手四次揮手其實才是xx。

最後直接反對這個答案:


http是應用層協議,主要依賴於運輸層TCP協議(HTTP協議沒有規定具體使用哪個運輸層協議)。

tcp連接建立和斷開方式涉及到客戶端和伺服器端的埠,緩存等資源的分配與釋放問題。

建立連接時,主動連接方(客戶端)向伺服器請求建立連接(SYN),伺服器端收到後會給客戶端響應(ack),表示它這邊準備好連接了,但是他要確保客戶端收到了它的響應,於是需要第三個數據包 客戶端向伺服器端確認(ack),這樣相互確認之後伺服器端就可以給這條連接分配必要的埠、緩存等資源。 假設只有兩次數據包交換就分配資源,若伺服器端響應客戶端連接請求的包丟失了,客戶端會認為伺服器未響應,放棄本次連接請求,而伺服器端之前分配的資源就會被閑置浪費。 更多次的報文交換是可以的,但是完全沒有必要。 三次交換是建立連接所需最少的數量。

斷開連接時, 任務先完成的一方(這裡假設是客戶端)會向另一方(即服務端)發送斷開請求(FIN),表示自己這一方傳輸任務已經完成,伺服器端收到後會響應。 但是這裡 伺服器端傳送給客戶端的數據可能還沒有完,他需要維持當前連接來完成剩餘的傳輸任務,即客戶端還不能釋放當前連接所需的資源。當服務端數據傳輸任務完成後,他會向客戶端發送斷開請求(FIN),客戶端響應。 這樣雙方相互確認數據傳輸完成,可以斷開連接,分別釋放當前連接佔用的系統資源,而不是提前釋放導致傳輸的數據丟失。

(手機碼字,有誤的地方請指正 :)....)


面試官胡說罷了,http 一個應用層協議,握手個毛?

你說因為面試官想問 tcp 握手所以情有可原?

這也是扯淡,誰說 http 就不能走串口了?


你準備好了么?好了,來吧。來咯

窩滿足了,好的。我也滿足了,好的。


是時候放出我做的一張圖片了


了解一些基礎知識怎麼就變成刁難了。

你寫個app需要用高數,線代,大物的知識嗎?難道你大學不學這些能畢業然後去面試?


也是來湊個熱鬧,實名反對當前高票 @荒炎 答案。

一派胡言,誤人子弟!!

理由在評論區已經有人指出,而原回答有很明顯的誤導嫌疑,如果不是純心誤導題主,那就是其本身水平堪憂。

在這種級別的計算機知識,圈內人都有共識,哪些是必須懂的,哪些是可以拿來考面試人的。像三次握手這種作為基礎知識,最適合管中窺豹,藉此了解面試者基礎如何進而了解其他方面水平。

原答主不但舉例不當,還涉嫌對題主所說的面試官的人身攻擊。現在居然還舉報摺疊…

真神奇!


某些高票回答看的我都驚了,tcp三次握手是計算機網路課程最基本的知識點吧?面試的時候還不讓問了?原因是這些知識點在職位上用不到!?!驚了驚了。那你說說計算機組成體系結構之類的你用得到?用他們的理論就是只要我不是畢業就去寫彙編,去搞os,去做底層那面試官就不能問我這一類問題,不然就是膚淺了?傻逼了?我靠,那面試官真的日了吉娃娃了。說到底還是在於某些人不知道這些基礎知識的必要性,只看到表面的,看不到隱藏的,學會這個是也許對於你的工作沒有作用,但這個問題能讓面試官了解你是否有一個良好的知識基礎,畢竟這個問題只要計算機網路課程能考過60的都了解一點,而一個專業人如果專業課學的這麼差你確定他能很出色?當然如果是自學的,或者轉行的也許沒有好的基礎,但這些問題面試官自然有他的思考,沒答上網路知識,如果沒有人比你更好他自然會選擇你,因為他知道你的職位不需要用到網路知識,所以沒回答上也沒事,但如果有和你一樣的人,且回答上了網路知識那自然更優於你,所以這些問題面試官問的沒問題,他能顯示出面試者的具體水平,更速食麵試官篩選。


不算叼難吧,可以簡單的回答, 因為http協議基於tcp協議實現,tcp協議提供可靠的長連接服務,所以需要通過3次握手和4次握手來實現可靠的建立連接和關閉連接。


看到這些回答很是氣人,有沒有。有時候面試官問的這些問題,是要面試高級開發或者架構之類的你一個程序員當然沒法理解。


我覺得你可能記錯了,面試官問的應該是TCP的三次握手與四次揮手。下面回答一下為什麼需要三次握手與四次揮手。

先看一下三次握手的圖

(圖片來自網路)

具體的過程我就不詳細介紹了,這裡解釋一下為什麼要設計成三次傳輸信息,為什麼在伺服器回傳確認報文(SYN=1,ACK=1,seq=y,ack=x+1)後還需要客戶端發送對此「確認」的確認報文連接才能建立?

這裡主要是為了防止伺服器收到「已失效的連接請求的報文段」這種情況:

客戶端發送了一個連接請求,如果採用」兩次握手「(即伺服器收到連接請求後回傳對請求報文的確認報文後立即進入連接狀態準備接受數據報文段),那麼考慮一種異常情況:

客戶端首先發送一個連接請求報文段A,但是由於經過的網路結點速率太慢,這個請求連接報文滯留在網路中了,客戶端在超時後重傳請求報文段B,然後這次收到了伺服器的確認報文,在完成了數據傳輸後斷開連接。而斷開連接後伺服器突然又收到了此前滯留在網路中的連接請求報文段A,此時伺服器以為客戶端又要請求建立連接,而根據我們此時假設的「兩次握手」協議,那麼伺服器此時就回傳此連接請求報文的確認報文(ack=x+1),同時立馬進入連接建立階段。但是客戶端並沒有發送序列號為x的請求報文(此前的連接已經正常的關閉了)。因此客戶端並不會進入連接狀態,不會有數據發送,於是伺服器就在那傻等著了。這樣會造成資源的浪費。

而使用「三次握手」協議,伺服器在沒有接受到對伺服器確認報文的「確認報文「時是不會建立連接狀態的,也就避免了上述的情況。

再看一下四次揮手(圖片來自Google,侵刪,其中標誌位的數據有些問題但是大概是這麼個意思)

伺服器在接受到客戶端的連接釋放報文段後回傳確認報文進入CLOSED_WAIT狀態,因為此時雖然客戶端沒有數據發送了(發送連接釋放報文段就意味著我沒有數據發送了),但是伺服器端可能還有數據傳送,因此此時伺服器端發送的是ACK=1報文也就是確認報文段,告訴客戶端我收到你的釋放請求了,但是我還有數據要給你,你等著接受數據;在伺服器段發送完數據後伺服器端發送連接釋放請求報文段(FIN=1,seq=v)在收到客戶端的對此連接釋放請求報文段的確認後(這裡上圖有一些不準確,最後的確認報文段應該是 ACK=1,ack=v+1)關閉此次連接,客戶端再等待一個TIME_WAIT時間後進入關閉狀態(這裡是為了確保此前的對伺服器的關閉連接報文段的確認報文段能夠到達伺服器端,如果這個確認報文段丟失了,那麼伺服器會重傳關閉連接請求的報文,這樣客戶端就可以重新傳送對關閉請求的確認,同時重新對TIME_WAIT計時,確保連接能夠正常的關閉)。

以上是我的理解,如果有不對的地方,歡迎各位指正!


推薦閱讀:

如何評價thinkphp5.0?
為什麼rsa加密時我把密鑰長度設成256位,太長的字元串加密就會出錯?
php如何實現數組合併但鍵值相同的數組不互相覆蓋?
學哪種編程語言能保住一頭秀髮?
.net的網站數據,可以轉到php程序里用嗎?

TAG:PHP | HTTP |