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的更多內容!
推薦閱讀:
- 詳解Idea中HTTP Client請求測試工具的使用
- 對Golang中的FORM相關字段理解
- 使用Spring處理x-www-form-urlencoded方式
- 使用flask如何獲取post請求參數
- Python 實現一個簡單的web服務器