Java面試題沖刺第六天–網絡編程1

面試題1:說一下TCP連接的三次握手和四次揮手吧

正經回答:

握手:TCP連接

揮手:TCP斷開

三次握手:

首先,三次握手的本質是確認通信雙方(Client端、Server端)收發數據的能力

三次握手其實就是指:建立一個TCP連接時,需要客戶端和服務器總共發送3個包,通過這三個請求包,來確認雙方(Client、Server)的接收能力和發送能力是否正常,同時,指定自己的初始化序列號為後面的可靠性傳送做準備。實質上就是連接服務器指定端口,建立TCP連接,並同步連接雙方的序列號和確認號,交換TCP窗口大小信息。

註:剛開始客戶端處於 Closed 的狀態,服務端處於 Listen 狀態。

三次握手(連接)流程白話文介紹:

我和女朋友養瞭一隻信鴿來傳信,今天我要試一試好不好使,不好使晚上我就準備吃燒烤。

  • 第一次握手:我把信(第一封信)綁在鴿子腿上發給女朋友,如果女朋友收到瞭,就確定瞭我的發件能力和她的收件能力沒問題;
  • 第二次握手:然後女朋友給我回信(第二封信),我如果收到瞭,說明我的收件能力和她的發件能力沒問題;
  • 第三次握手:然而此時女朋友還不知道她的發件能力和我的收件能力是否正常;因此我還要給他發(第三封信)說明,收到後最終決定晚上去吃烤魚。信鴿:臥槽,真tm累,你們手機都是假的吧。

在這裡插入圖片描述

三次握手理論流程:

  • 第一次握手:客戶端將標志位SYN置為1,隨機產生一個值seq=J,並將該數據包發送給服務器端,客戶端進入SYN_SENT狀態,等待服務器端確認。
  • 第二次握手:服務器端收到數據包後由標志位SYN=1知道客戶端請求建立連接,服務器端將標志位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給客戶端以確認連接請求,服務器端進入SYN_RCVD狀態。
  • 第三次握手:客戶端收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標志位ACK置為1,ack=K+1,並將該數據包發送給服務器端,服務器端檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,客戶端和服務器端進入ESTABLISHED狀態,完成三次握手,隨後客戶端與服務器端之間可以開始傳輸數據瞭。

四次揮手:

四次揮手即終止TCP連接,就是指斷開一個TCP連接時,需要客戶端和服務端總共發送4個包以確認連接的斷開。在socket編程中,這一過程由客戶端或服務端任一方執行close來觸發。

由於TCP連接是全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成數據發送任務後,發送一個FIN來終止這一方向的連接,收到一個FIN隻是意味著這一方向上沒有數據流動瞭,即不會再收到數據瞭,但是在這個TCP連接上仍然能夠發送數據,直到這一方向也發送瞭FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉。

在這裡插入圖片描述

四次揮手理論流程

中斷連接端可以是客戶端,也可以是服務器端。

第一次揮手:客戶端發送一個FIN=M,用來關閉客戶端到服務器端的數據傳送,客戶端進入FIN_WAIT_1狀態。意思是說”我客戶端沒有數據要發給你瞭”,但是如果你服務器端還有數據沒有發送完成,則不必急著關閉連接,可以繼續發送數據。

第二次揮手:服務器端收到FIN後,先發送ack=M+1,告訴客戶端,你的請求我收到瞭,但是我還沒準備好,請繼續你等我的消息。這個時候客戶端就進入FIN_WAIT_2 狀態,繼續等待服務器端的FIN報文。

第三次揮手:當服務器端確定數據已發送完成,則向客戶端發送FIN=N報文,告訴客戶端,好瞭,我這邊數據發完瞭,準備好關閉連接瞭。服務器端進入LAST_ACK狀態。

第四次揮手:客戶端收到FIN=N報文後,就知道可以關閉連接瞭,但是他還是不相信網絡,怕服務器端不知道要關閉,所以發送ack=N+1後進入TIME_WAIT狀態,如果Server端沒有收到ACK則可以重傳。服務器端收到ACK後,就知道可以斷開連接瞭。客戶端等待瞭2MSL後依然沒有收到回復,則證明服務器端已正常關閉,那好,我客戶端也可以關閉連接瞭。最終完成瞭四次握手。

深入追問:

追問1:為什麼連接的時候是三次握手,關閉的時候卻是四次握手?

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

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

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

若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出瞭故障,接著就關閉連接。

面試題2:常見的HTTP狀態碼有哪些?

正經回答:

HTTP狀態碼表示客戶端HTTP請求的返回結果、標識服務器處理是否正常、表明請求出現的錯誤等。

狀態碼的類別:

狀態碼 原因
1XX Informational(信息性狀態碼) 接受的請求正在處理
2XX Success(成功狀態碼) 請求正常處理完畢
3XX Redirection(重定向狀態碼) 需要進行附加操作以完成請求
4XX Client Error(客戶端錯誤狀態碼) 服務器無法處理請求
5XX Server Error(服務器錯誤狀態碼) 服務器處理請求出錯
狀態碼 原因
2XX 成功(這系列表明請求被正常處理瞭)
200 OK,表示從客戶端發來的請求在服務器端被正確處理
204 No content,表示請求成功,但響應報文不含實體的主體部分
206 Partial Content,進行范圍請求成功
狀態碼 原因
3XX 重定向(表明瀏覽器要執行特殊處理)
301 moved permanently,永久性重定向,表示資源已被分配瞭新的 URL
302 found,臨時性重定向,表示資源臨時被分配瞭新的 URL
303 see other,表示資源存在著另一個 URL,應使用 GET 方法獲取資源
304 not modified,表示服務器允許訪問資源,但請求未滿足條件的情況(與重定向無關)
307 temporary redirect,臨時重定向,和302含義類似,但是期望客戶端保持請求方法不變向新的地址發出請求
狀態碼 原因
4XX 客戶端錯誤
400 bad request,請求報文存在語法錯誤
401 unauthorized,表示發送的請求需要有通過 HTTP 認證的認證信息
403 forbidden,表示對請求資源的訪問被服務器拒絕,可在實體主體部分返回原因描述
404 not found,表示在服務器上沒有找到請求的資源
狀態碼 原因
5XX 服務器錯誤
500 internal sever error,表示服務器端在執行請求時發生瞭錯誤
501 Not Implemented,表示服務器不支持當前請求所需要的某個功能
503 service unavailable,表明服務器暫時處於超負載或正在停機維護,無法處理請求

面試題3:先說說GET和POST請求有哪些區別吧?

正經回答:

GET請求在URL中傳送的參數是有長度限制的,而POST沒有。

GET比POST更不安全,因為參數直接暴露在URL上,所以不能用來傳遞敏感信息。而POST數據不會顯示在URL中。是放在Request body中。

對參數的數據類型,GET隻接受ASCII字符,而POST沒有限制。

GET請求參數會被完整保留在瀏覽器歷史記錄裡;相反,POST請求參數也不會被瀏覽器保留。

GET請求隻能進行url編碼(application/x-www-form-urlencoded),而POST支持多種編碼方式。

GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置。

GET在瀏覽器回退時是無害的,而POST會再次提交請求。

深入追問:

追問1:那Get請求有Request body麼?如果有的話參數可以像Post請求一樣放在裡面麼?

其實吧,GET和POST在本質上沒有區別,都是HTTP協議中的兩種發送請求的方法。而HTTP呢,是基於TCP/IP的關於數據如何在萬維網中如何通信的協議。

萬維網:簡稱WWW,是World Wide Web的簡稱,也稱為Web、3W等

HTTP的底層是TCP/IP。所以GET和POST的底層也是TCP/IP,也就是說,GET/POST都是TCP鏈接。

GET和POST能做的事情是一樣一樣的。你要給GET加上request body,給POST帶上url參數,技術上是完全行的通的。

  • 舉個例子吧:

TCP就像汽車,我們用TCP來運輸數據,它很可靠,從來不會發生丟件少件的現象。

但是如果路上跑的全是看起來一模一樣的汽車,那這個世界看起來是一團混亂,送急件的汽車可能被前面滿載貨物的汽車攔堵在路上,整個交通系統一定會癱瘓。

在這裡插入圖片描述

為瞭避免這種情況發生,交通規則HTTP誕生瞭。HTTP給汽車運輸設定瞭好幾個服務類別,包括GET, POST, PUT等等,

HTTP規定,當執行GET請求的時候,要給汽車貼上GET的標簽(設置method為GET),而且要求把傳送的數據放在車頂上(url中)以方便記錄。

如果是POST請求,就要在車上貼上POST的標簽,並把貨物放在車廂裡(request body中)。

當然,你也可以在用GET的時往車廂內偷偷藏點貨物,但這並不不光彩;也可以在POST的時候在車頂上也放一些數據,也會讓人覺得傻乎乎的。

在這裡插入圖片描述

HTTP隻是個行為準則,而GET和POST本質上就是TCP鏈接,並無差別。但是由於HTTP的規定和瀏覽器/服務器的限制,導致他們在應用過程中體現出一些不同。

追問2:那你剛才說的URL中傳送參數的長度限制在Get和Post中都是怎麼樣的呢?

其實在Web中啊,還有另一個重要的角色:運輸公司。

不同的瀏覽器Client端(發起http請求)和服務器server端(接受http請求)就是不同的運輸公司。

雖然理論上,你可以在車頂上無限的堆貨物(url中無限加參數)。但是運輸公司可不傻,裝貨和卸貨也是有很大成本的,他們會限制單次運輸量來控制風險,數據量太大對瀏覽器和服務器都是很大負擔。

業界不成文的規定是:(大多數)瀏覽器通常都會限制url長度在2K個字節,而(大多數)服務器最多處理64K大小的url。

超過的部分,恕不處理。如果你用GET服務,在request body偷偷藏瞭數據,不同服務器的處理方式也是不同的,有些服務器會幫你卸貨,讀出數據,有些服務器直接忽略。

所以,雖然GET可以帶request body,卻不能保證一定能被接收到。

在這裡插入圖片描述

我之前處理過一個bug,用戶反應查詢沒有響應,同事查瞭日志後才發現有幾個參數都是undefined,很奇怪,最後發現原來是因為Get請求第一個查詢參數太長瞭,導致URL後面的部分服務器無法接收 ,後來把請求改成post,將參數放在request body後就可以瞭。

追問3:那麼你知道Get、Post請求發送的數據包有什麼不同嗎?

嗯嗯,是這樣的,GET請求時產生一個TCP數據包;POST請求時產生兩個TCP數據包。

  • GET:瀏覽器會把http header和data一並發送出去,服務器響應200(返回數據);
  • POST:瀏覽器先發送header,服務器響應100 continue,瀏覽器再發送data,服務器響應200 OK(返回數據)。

就像是GET隻需要汽車跑一趟就把貨送到瞭,而POST得跑兩趟,第一趟,先去和服務器打個招呼“老鐵,我等下要送一批貨來,你們準備接收一下哈”,然後再回頭把貨送過去。

因為POST需要兩步,理論上時間上消耗的要多一點,看起來GET比POST更有效。但並不是,後來發現原來是個坑。在我看來:

1.GET與POST都有自己的語義,不能隨便混用。

2.據研究,在網絡環境好的情況下,發一次包的時間和發兩次包的時間差別基本可以無視。而在網絡環境差的情況下,兩次包的TCP在驗證數據包完整性上,有非常大的優點。

3.並不是所有瀏覽器都會在POST中發送兩次包,Firefox就隻發送一次。我去年用Chrome瀏覽器測試發現也是隻發送一次,所以我認為Get、POST性能差可以人為忽略。

總結

本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: