轉自: https://mp.weixin.qq.com/s/8Q50wJlQF9jUovMNPgc-QA
「本篇總結了 12 道最多見的計算機網絡面試題,並給出了一些本身的見解,如有不妥之處萬望指正。html
在講三次握手以前首先要介紹 TCP 報文中兩個重要的字段:一個是序號字段,另外一個是確認號字段,這兩個字段將在握手階段以及整個信息傳輸過程起到重要做用。web
第一步:客戶端 TCP 向服務端的 TCP 發送一個不帶額外數據的特殊 TCP 報文段,該報文段的 SYN 標誌位會被置 1,因此把它稱爲 SYN 報文段。這時客戶端會選取一個初始序列號(假設爲 client_num),並將此編號放置在序號字段中。該報文段會被封裝在一個IP數據報中發送給服務器。面試
第二步:服務器接收到 SYN 報文段後,會爲該 TCP 分配緩存和變量,併發送容許鏈接的確認報文。在容許鏈接的報文中,SYN 標誌位仍被置爲 1,確認號字段填的是 client_num + 1 的值。最後服務端也會選取一個 server_num 存放到序號字段中,這個報文段稱爲 SYNACK 報文段。redis
第三步:在接收到 SYNACK 報文段後,客戶端最後也要向服務端發送一個確認報文,這個報文和前兩個不同,SYN 標誌位置 0,在確認號字段中填上 server_num + 1 的值,而且這個報文段能夠攜帶數據。一旦完成這 3 個步驟,客戶端和服務器之間就能夠相互發送包含數據的報文了。算法
若是不是三次握手,兩次的話,服務器就不知道客戶端是否接收到了本身的 SYNACK 報文段,從而沒法創建鏈接;四次握手就顯得多餘了。數據庫
什麼 SYN 是洪泛攻擊? 在 TCP 的三次握手機制的第一步中,客戶端會向服務器發送 SYN 報文段。服務器接收到 SYN 報文段後會爲該 TCP分 配緩存和變量,若是攻擊分子大量地往服務器發送 SYN 報文段,服務器的鏈接資源終將被耗盡,致使內存溢出沒法繼續服務。小程序
解決策略:當服務器接受到 SYN 報文段時,不直接爲該 TCP 分配資源,而只是打開一個半開的套接字。接着會使用 SYN 報文段的源 Id,目的 Id,端口號以及只有服務器本身知道的一個祕密函數生成一個 cookie,並把 cookie 做爲序列號響應給客戶端。後端
若是客戶端是正常創建鏈接,將會返回一個確認字段爲 cookie + 1 的報文段。接下來服務器會根據確認報文的源 Id,目的 Id,端口號以及祕密函數計算出一個結果,若是結果的值 + 1 等於確認字段的值,則證實是剛剛請求鏈接的客戶端,這時候才爲該 TCP 分配資源瀏覽器
這樣一來就不會爲惡意攻擊的 SYN 報文段分配資源空間,避免了攻擊。緩存
當客戶端要服務器斷開鏈接時,客戶端 TCP 會向服務器發送一個特殊的報文段,該報文段的 FIN 標誌位會被置 1,接着服務器會向客戶端發送一個確認報文段。而後服務器也會客戶端發送一個 FIN 標誌位爲 1 的終止報文段,隨後客戶端回送一個確認報文段,服務器當即斷開鏈接。客戶端等待一段時間後也斷開鏈接。
其實四次揮手的過程是很容易理解的,因爲 TCP 協議是全雙工的,也就是說客戶端和服務端均可以發起斷開鏈接。兩邊各發起一次斷開鏈接的申請,加上各自的兩次確認,看起來就像執行了四次揮手。
爲何要有 TIME_WAIT 狀態?由於客戶端最後向服務器發送的確認 ACK 是有可能丟失的,當出現超時,服務端會再次發送 FIN 報文段,若是客戶端已經關閉了就收不到了。還有一點是避免新舊鏈接混雜。
大量 CLOSE_WAIT 表示程序出現了問題,對方的 socket 已經關閉鏈接,而我方忙於讀或寫沒有及時關閉鏈接,須要檢查代碼,特別是釋放資源的代碼,或者是處理請求的線程配置。
詳情可參考如下博客:https://www.cnblogs.com/sunxucool/p/3449068.html
image
從上面的圖能夠看到滑動窗口左邊的是已發送而且被確認的分組,滑動窗口右邊是尚未輪到的分組。滑動窗口裏面也分爲兩塊,一塊是已經發送可是未被確認的分組,另外一塊是窗口內等待發送的分組。隨着已發送的分組不斷被確認,窗口內等待發送的分組也會不斷被髮送。整個窗口就會往右移動,讓還沒輪到的分組進入窗口內。
能夠看到滑動窗口起到了一個限流的做用,也就是說當前滑動窗口的大小決定了當前 TCP 發送包的速率,而滑動窗口的大小取決於擁塞控制窗口和流量控制窗口的二者間的最小值。
接着就講講什麼是流量控制窗口,什麼是擁塞控制窗口。
先講流量控制:
TCP 是全雙工的,客戶端和服務器都可做爲發送方或接收方,咱們如今假設一個發送方向接收方發送數據的場景來說解流量控制。首先咱們的接收方有一塊接收緩存,當數據來到時會先把數據放到緩存中,上層應用等緩存中有數據時就會到緩存中取數據。假如發送方沒有限制地不斷地向接收方發送數據,接收方的應用程序又沒有及時把接收緩存中的數據讀走,就會出現緩存溢出,數據丟失的現象,爲了解決這個問題,咱們引入流量控制窗口。
假設應用程序最後讀走的數據序號是 lastByteRead,接收緩存中接收到的最後一個數據序號是 lastByteRcv,接收緩存的大小爲 RcvSize,那麼必需要知足 lastByteRcv - lastByteRead <= RcvSize 才能保證接收緩存不會溢出,因此咱們定義流量窗口爲接收緩存剩餘的空間,也就是 Rcv = RcvSize - (lastByteRcv - lastByteRead)。只要接收方在響應 ACK 的時候把這個窗口的值帶給發送方,發送方就能知道接收方的接收緩存還有多大的空間,進而設置滑動窗口的大小。
接着講解擁塞控制:
擁塞控制是指發送方先設置一個小的窗口值做爲發送速率,當成功發包並接收到 ACK 時,便以指數速率增大發送窗口的大小,直到遇到丟包(超時/三個冗餘 ACK ),才中止並調整窗口的大小。這麼作能最大限度地利用帶寬,又不至於讓網絡環境變得太過擁擠。
最終滑動窗口的值將設置爲流量控制窗口和擁塞控制窗口中的較小值。
HTTP 和 HTTPS 的主要區別在於 HTTP 協議傳遞的是明文數據,而 HTTPS 傳遞的是加密過的數據,也就是說 HTTPS 更具備安全性。也正由 HTTPS 須要保證安全性,因此它的性能要比 HTTP 差一點。
單說安全性確定是不夠的,我打算擴展講一下 HTTPS 是怎麼解決安全性問題的,經過這些 HTTP 沒有機制,反映出 HTTPS 與 HTTP 的區別。下面嘗試把 HTTPS 加密的過程推導出來。推導過程不涉及複雜的實現細節:
假設如今 A 和 B 要進行安全的通訊,那麼究竟怎樣纔算是安全的通訊?很天然地會想到:A 和 B 之間傳遞數據,這些數據只有 A 和 B 纔看得懂,中間人就算截取了信息但也看不懂,這纔算得上安全。
爲了能讓 A 和 B 才能看懂,就必需要對數據進行加密,並且首先想到的就是對稱加密。對稱加密的意思是 A 和 B 各持有一個相同的密鑰,它們傳遞信息時會用密鑰給信息加密,在消息到達端給消息解密,完成安全通訊。
在對稱加密中又會涉及到加密算法的選擇問題。現實世界中,一般是多個客戶端面向一個服務器的狀況,不可能讓每一個客戶端和服務器之間都採用相同的加密算法,若是是這樣那和沒加密差很少。因此註定每一個客戶端和服務器之間都會採用不一樣的加密方式。
要想對不一樣的機器使用不一樣的加密方式,最直接想到的就是使用隨機數。也就說客戶端和服務器之間每次都基於一個隨機數產生加密算法。(具體實現時爲了保證隨機,用到還不止一個隨機數)
這個產生加密算法的過程稱之爲協商,如今問題是協商的過程是透明的,也就是說中間人能夠截獲協商的過程,從而知道咱們的加密方式。爲了解決這個問題,咱們須要對協商的過程進行加密。
之因此能來到這一步,是由於咱們一開始就選擇使用了對稱加密,也就說一開始的對稱加密緻使瞭如今的問題,因此這時咱們不能再使用對稱加密了,不然會陷入死循環。
在密碼學領域,還有一種加密過程叫非對稱加密,它的邏輯是這樣的:通訊雙方一方持有私鑰,一方持有公鑰,通過私鑰加密的信息,都能經過公鑰進行解密。可是通過公鑰加密的數據,只有私鑰能夠解密。
按照非對稱加密的規則,咱們讓服務器持有私鑰,讓客戶端持有公鑰。這樣就能保證客戶端給服務器發送消息的時候是安全的(相反,服務器給客戶端發送消息就是不安全的),咱們能夠把協商時重要的邏輯安排在客戶端給服務器發送信息的過程當中,從而保證了協商過程的安全性。
如今用非對稱加密算法解決了協商的安全問題,可是非對稱加密的前提是客戶端須要得到公鑰,這又是一個問題了,客戶端與服務器打交道以前是互不知道雙方身份的,怎麼才能讓客戶端得到公鑰呢?
也就只有兩種辦法:
方法2顯然是不行的,尚且不說多了一個訪問節點,如何找到公共服務器的地址也是一個待解決的問題,因此仍是使用方法 1。
可是方法 1 存在一個問題:若是中間人把服務器發送給客戶端的公鑰調包了怎麼辦?也就是說客戶端沒法知道發送公鑰的是不是正真的服務器。
客戶端沒法辨識服務端和中間人的問題稱爲「身份驗證」問題,也就是說咱們須要爲服務器向客戶端發送公鑰的過程進行加密。
這下完了,以前咱們因遇到對稱加密的瓶頸選擇了非對稱加密,如今使用非對稱加密也遇到了瓶頸。顯然這兩種加密方式都是不可用的了,不然會再次陷入死循環。
接下來咱們只好經過第三方機構的介入,解決這個問題。首先咱們本身保存有第三方權威機構的公鑰,而後第三方機構使用私鑰對服務器將要發送給客戶端的公鑰進行加密,客戶端接收到這個經加密的公鑰後(數字證書),就能經過本身保存的第三方機構公鑰進行解密。
「到這裏爲止,咱們解釋了 HTTPS 中使用到的對稱加密,非對稱加密,CA,數字證書的概念,可是還差一個叫數字簽名的概念沒有解釋。
在現實生活中,CA 不單止會給咱們正常公司發放證書,還會給中間人的壞公司發放證書,若是中間人把發放的證書調包了怎麼辦?這時咱們仍能用 CA 的私鑰進行解密,可是證書已經被調包了。
那麼客戶端怎樣驗證證書的真僞呢?答案是證書自己會告訴客戶端如何辨認真僞。比方說證書上面有一個證書編號,還有一個如何計算證書編號的方法,客戶端能夠根據計算證書編號的方法計算出本身要得到的證書的編號,而後把這個編號和證書上的編號進行比對,若是同樣證實沒有被調包。
這裏的證書編號指的就是數字簽名,證書指的就是數字證書。
總結一下 HTTPS :HTTPS 想要保證客戶端與服務器之間的通訊安全,就得使用對稱加密算法進行加密。協商對稱加密算法的過程經過非對稱加密算法來保證。在非對稱加密算法中,客戶端得到公鑰的過程須要第三方機構(CA)經過頒發數字證書保證安全性。
總得來講經過這一系列機制協商出了一個對稱加密算法後,客戶端與服務器之間就能經過該算法進行安全的通訊了。
TCP 是面向鏈接的,使用 TCP 鏈接須要通過三次握手,斷開鏈接須要通過四次揮手。相比於 TCP,UDP 則是面向無鏈接的,它像一個隨時能夠進行通訊的協議,只要接收方建立了 Socket,設置了監聽端口,就能接收到到達的 UDP 包,而且能隨時創建 Socket 進行發包。
TCP 能夠對數據的發送速率做控制,它具備流量控制和擁塞控制機制,經過控制滑動窗口的大小控制數據的發送速率。而 UDP 理論上是能夠無限地向網絡中發數據包的,它的發送速率取決它的應用程序設置。
TCP 保證可靠的數據傳輸,它根據速率控制,重傳控制,還有數據校驗保證提供可靠的數據傳輸服務。而 UDP 只是保證盡力而爲地傳輸數據,並不保證數據的可靠性。
TCP 的報文段頭部佔 20 各字節,比 UDP 的報文段頭部多 12 個字節。消耗的資源更多。因爲創建鏈接有握手的機制,TCP 的使用效率會比 UDP 要低一些。
我的以爲這個問題還能夠擴展一下,試想一下在鍵入 URL 以前,也就是剛開完機的時候,須要聯網,而後才能上網。這個階段包括了獲取本機 IP 地址,獲取 DNS 服務器 IP 地址,得到網關路由器 IP 和 MAC 地址等操做,把這些一塊兒答上去會不會好一些?如下是回答:
首先咱們須要準備一個 DHCP 報文,封裝在一個 UDP 報文段中,裏面包括本機端口號 68 和目的端口號 67,而後到網絡層封裝成數據包裏面包括了本機的初始 IP 0.0.0.0,和廣播地址 255.255.255.255。接着到鏈路層封裝成鏈路層幀。裏面包括廣播地址和本機網卡的 MAC 地址。最後發送到本地局域網中
這個數據包最終會被局域網中的 DHCP 服務器發現(有可能有多個 DHCP 服務器),DHCP 服務器會把可用的 IP 地址返回給咱們的主機。而後操做系統選擇一個 IP 地址併發送給 DHCP 服務器,最後 DHCP 服務器會返回一個包含本機 IP,DNS 服務器 IP,網關路由器 IP 的報文。
接下來咱們須要經過網關路由器的 IP 地址去得到網關路由器的 MAC 地址,這樣咱們才能夠把獲取網站 IP 的 DNS 請求報文由網關發送給 DNS 服務器。這時候咱們須要準備一個 ARP 請求報文,請求獲取網關路由器的 MAC 地址,這個報文一樣是以廣播的方式發送到局域網中,網關路由器接受到請求報文就會把本身的 MAC 地址返回給本機。
接下來一切都準備好了,能夠開始講鍵入 URL 以後的事情了:
首先咱們要訪問 DNS 服務器得到網站對應的 IP 地址,這時咱們須要把 DNS 報文封裝到一個 UDP 報文中,進而封裝到網絡層的數據包中,填上源 IP,目的 DNS 服務器 IP 地址。接着封裝鏈路層,填上網卡 MAC 地址和網關路由器 MAC 地址。接下來這個 DNS 請求報文就會經網關路由器發送給 DNS 服務器。
咱們假設 DNS 服務器緩存有該網站的 IP 地址,(若是沒有緩存會進一步向更高級的DNS服務器索要IP地址)。接着 DNS 服務器會返回該域名的 IP 地址。
拿到了該網站的 IP 地址後就能夠與該網站的服務器創建 TCP 鏈接了。創建 TCP 鏈接須要通過三次握手,過程以下:(更詳細的過程在開頭)
本機的 TCP 首先生成一個不帶任何數據,SYN 標誌位爲 1,序號字段假設爲client_num 的 TCP 報文,通過下層一系列網絡棧後發送給目的 ip 服務器。
該服務器接受到 TCP 請求報文後,會迴應一個贊成鏈接的 TCP 報文,這個報文的 SYN 標誌位也會被置 1,序號字段假設爲 server_num,ACK 響應字段爲 client_num + 1。
接受到贊成鏈接的報文後,我方主機會進行響應,此次的 TCP 報文 SYN 位會被置0,序號字段爲 client_num + 1,ACK 響應字段爲 server + 1。而且此次的響應報文是能夠攜帶數據的。
在三次握手創建鏈接後,本機就能夠向服務器發送 HTTP 請求了,服務器接受到了請求會作出響應的響應,把請求的數據發送給本機瀏覽器,最終瀏覽器把服務器響應的數據渲染顯示出來,咱們就看到了五彩繽紛的網頁。
首先狀態碼的開頭不一樣表明不一樣的類型:
1xx:表明指示信息,表示請求已接收,繼續處理
2xx:表明成功,表示請求已被成功接收,理解,接受
3xx:重定向,表示完成請求必須進行進一步的操做
4xx:客戶端錯誤,請求有語法錯誤或請求沒法實現
5xx:服務器端錯誤,服務器未能實現合法的請求
常見狀態碼:
200 OK:正常返回信息
400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解
403 Forbidden:服務器收到請求,可是拒絕提供服務
404 Not Found:請求資源不存在,輸入了錯誤的URL
500 Internal Server Error:服務器發生不可預期錯誤
503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間後可能恢復正常
從 HTTP 報文層面來看,GET 請求將信息放在 URL,POST 將請求信息放在請求體中。這一點使得 GET 請求攜帶的數據量有限,由於 URL 自己是有長度限制的,而 POST 請求的數據存放在報文體中,所以對大小沒有限制。並且從形式上看,GET 請求把數據放 URL 上感受不太安全,而 POST 請求把數據放在請求體裏彷佛安全一些。實際上想要獲取 POST 請求中的內容仍是很容易的,所以二者在安全性上其實沒有太大差別,想要實現安全的信息傳輸仍是得靠 HTTPS。
從數據庫層面來看,GET 符合冪等性和安全性,而 POST 請求不符合。這個其實和 GET/POST 請求的做用有關。按照 HTTP 的約定,GET 請求用於查看信息,不會改變服務器上的信息;而 POST 請求用來改變服務器上的信息。正由於 GET 請求只查看信息,不改變信息,對數據庫的一次或屢次操做得到的結果是一致的,認爲它符合冪等性。安全性是指對數據庫操做沒有改變數據庫中的數據。
從其餘層面來看,GET 請求可以被緩存,GET 請求可以保存在瀏覽器的瀏覽記錄裏,GET 請求的 URL 可以保存爲瀏覽器書籤。這些都是 POST 請求所不具有的。緩存是 GET 請求被普遍應用的根本,他可以被緩存也是由於它的冪等性和安全性,除了返回結果沒有其餘多餘的動做,所以絕大部分的 GET 請求都被 CDN 緩存起來了,大大減小了 Web 服務器的負擔。
因爲 Http 協議是無狀態協議,若是客戶經過瀏覽器訪問 web 應用時沒有一個保存用戶訪問狀態的機制,那麼將不能持續跟蹤應用的操做。好比當用戶往購物車中添加了商品,web 應用必須在用戶瀏覽別的商品的時候仍保存購物車的狀態,以便用戶繼續往購物車中添加商品。
cookie 是瀏覽器的一種緩存機制,它可用於維持客戶端與服務器端之間的會話。因爲下面一題會講到 session,因此這裏要強調 cookie 會將會話保存在客戶端( session 則是把會話保存在服務端)
這裏以最多見的登錄案例講解cookie的使用過程:
首先用戶在客戶端瀏覽器向服務器發起登錄請求
登錄成功後,服務端會把登錄的用戶信息設置 cookie 中,返回給客戶端瀏覽器
客戶端瀏覽器接收到 cookie 請求後,會把 cookie 保存到本地(多是內存,也多是磁盤,看具體使用狀況而定)
之後再次訪問該 web 應用時,客戶端瀏覽器就會把本地的 cookie 帶上,這樣服務端就能根據 cookie 得到用戶信息了
session 是一種維持客戶端與服務器端會話的機制。可是與 cookie 把會話信息保存在客戶端本地不同,session 把會話保留在瀏覽器端。
咱們一樣以登錄案例爲例子講解 session 的使用過程:
首先用戶在客戶端瀏覽器發起登錄請求
登錄成功後,服務端會把用戶信息保存在服務端,並返回一個惟一的 session 標識給客戶端瀏覽器。
客戶端瀏覽器會把這個惟一的 session 標識保存在起來
之後再次訪問 web 應用時,客戶端瀏覽器會把這個惟一的 session 標識帶上,這樣服務端就能根據這個惟一標識找到用戶信息。
看到這裏可能會引發疑問:把惟一的 session 標識返回給客戶端瀏覽器,而後保存起來,之後訪問時帶上,這難道不是 cookie 嗎?
沒錯,session 只是一種會話機制,在許多 web 應用中,session 機制就是經過 cookie 來實現的。也就是說它只是使用了 cookie 的功能,並非使用 cookie 完成會話保存。與 cookie 在保存客戶端保存會話的機制相反,session 經過 cookie 的功能把會話信息保存到了服務端。
進一步地說,session 是一種維持服務端與客戶端之間會話的機制,它能夠有不一樣的實現。以如今比較流行的小程序爲例,闡述一個 session 的實現方案:
首先用戶登錄後,須要把用戶登錄信息保存在服務端,這裏咱們能夠採用 redis。好比說給用戶生成一個 userToken,而後以 userId 做爲鍵,以 userToken 做爲值保存到 redis 中,並在返回時把 userToken 帶回給小程序端。
小程序端接收到 userToken 後把它緩存起來,之後每當訪問後端服務時就把 userToken 帶上。
在後續的服務中服務端只要拿着小程序端帶來的 userToken 和 redis 中的 userToken 進行比對,就能肯定用戶的登錄狀態了。
通過上面兩道題的闡述,這道題就很清晰了
cookie 是瀏覽器提供的一種緩存機制,它能夠用於維持客戶端與服務端之間的會話
session 指的是維持客戶端與服務端會話的一種機制,它能夠經過 cookie 實現,也能夠經過別的手段實現。
若是用 cookie 實現會話,那麼會話會保存在客戶端瀏覽器中
而 session 機制提供的會話是保存在服務端的。
「本篇總結了 12 道最多見的計算機網絡面試題,並給出了一些本身的見解,如有不妥之處萬望指正。
在講三次握手以前首先要介紹 TCP 報文中兩個重要的字段:一個是序號字段,另外一個是確認號字段,這兩個字段將在握手階段以及整個信息傳輸過程起到重要做用。
第一步:客戶端 TCP 向服務端的 TCP 發送一個不帶額外數據的特殊 TCP 報文段,該報文段的 SYN 標誌位會被置 1,因此把它稱爲 SYN 報文段。這時客戶端會選取一個初始序列號(假設爲 client_num),並將此編號放置在序號字段中。該報文段會被封裝在一個IP數據報中發送給服務器。
第二步:服務器接收到 SYN 報文段後,會爲該 TCP 分配緩存和變量,併發送容許鏈接的確認報文。在容許鏈接的報文中,SYN 標誌位仍被置爲 1,確認號字段填的是 client_num + 1 的值。最後服務端也會選取一個 server_num 存放到序號字段中,這個報文段稱爲 SYNACK 報文段。
第三步:在接收到 SYNACK 報文段後,客戶端最後也要向服務端發送一個確認報文,這個報文和前兩個不同,SYN 標誌位置 0,在確認號字段中填上 server_num + 1 的值,而且這個報文段能夠攜帶數據。一旦完成這 3 個步驟,客戶端和服務器之間就能夠相互發送包含數據的報文了。
若是不是三次握手,兩次的話,服務器就不知道客戶端是否接收到了本身的 SYNACK 報文段,從而沒法創建鏈接;四次握手就顯得多餘了。
什麼 SYN 是洪泛攻擊? 在 TCP 的三次握手機制的第一步中,客戶端會向服務器發送 SYN 報文段。服務器接收到 SYN 報文段後會爲該 TCP分 配緩存和變量,若是攻擊分子大量地往服務器發送 SYN 報文段,服務器的鏈接資源終將被耗盡,致使內存溢出沒法繼續服務。
解決策略:當服務器接受到 SYN 報文段時,不直接爲該 TCP 分配資源,而只是打開一個半開的套接字。接着會使用 SYN 報文段的源 Id,目的 Id,端口號以及只有服務器本身知道的一個祕密函數生成一個 cookie,並把 cookie 做爲序列號響應給客戶端。
若是客戶端是正常創建鏈接,將會返回一個確認字段爲 cookie + 1 的報文段。接下來服務器會根據確認報文的源 Id,目的 Id,端口號以及祕密函數計算出一個結果,若是結果的值 + 1 等於確認字段的值,則證實是剛剛請求鏈接的客戶端,這時候才爲該 TCP 分配資源
這樣一來就不會爲惡意攻擊的 SYN 報文段分配資源空間,避免了攻擊。
當客戶端要服務器斷開鏈接時,客戶端 TCP 會向服務器發送一個特殊的報文段,該報文段的 FIN 標誌位會被置 1,接着服務器會向客戶端發送一個確認報文段。而後服務器也會客戶端發送一個 FIN 標誌位爲 1 的終止報文段,隨後客戶端回送一個確認報文段,服務器當即斷開鏈接。客戶端等待一段時間後也斷開鏈接。
其實四次揮手的過程是很容易理解的,因爲 TCP 協議是全雙工的,也就是說客戶端和服務端均可以發起斷開鏈接。兩邊各發起一次斷開鏈接的申請,加上各自的兩次確認,看起來就像執行了四次揮手。
爲何要有 TIME_WAIT 狀態?由於客戶端最後向服務器發送的確認 ACK 是有可能丟失的,當出現超時,服務端會再次發送 FIN 報文段,若是客戶端已經關閉了就收不到了。還有一點是避免新舊鏈接混雜。
大量 CLOSE_WAIT 表示程序出現了問題,對方的 socket 已經關閉鏈接,而我方忙於讀或寫沒有及時關閉鏈接,須要檢查代碼,特別是釋放資源的代碼,或者是處理請求的線程配置。
詳情可參考如下博客:https://www.cnblogs.com/sunxucool/p/3449068.html
image
從上面的圖能夠看到滑動窗口左邊的是已發送而且被確認的分組,滑動窗口右邊是尚未輪到的分組。滑動窗口裏面也分爲兩塊,一塊是已經發送可是未被確認的分組,另外一塊是窗口內等待發送的分組。隨着已發送的分組不斷被確認,窗口內等待發送的分組也會不斷被髮送。整個窗口就會往右移動,讓還沒輪到的分組進入窗口內。
能夠看到滑動窗口起到了一個限流的做用,也就是說當前滑動窗口的大小決定了當前 TCP 發送包的速率,而滑動窗口的大小取決於擁塞控制窗口和流量控制窗口的二者間的最小值。
接着就講講什麼是流量控制窗口,什麼是擁塞控制窗口。
先講流量控制:
TCP 是全雙工的,客戶端和服務器都可做爲發送方或接收方,咱們如今假設一個發送方向接收方發送數據的場景來說解流量控制。首先咱們的接收方有一塊接收緩存,當數據來到時會先把數據放到緩存中,上層應用等緩存中有數據時就會到緩存中取數據。假如發送方沒有限制地不斷地向接收方發送數據,接收方的應用程序又沒有及時把接收緩存中的數據讀走,就會出現緩存溢出,數據丟失的現象,爲了解決這個問題,咱們引入流量控制窗口。
假設應用程序最後讀走的數據序號是 lastByteRead,接收緩存中接收到的最後一個數據序號是 lastByteRcv,接收緩存的大小爲 RcvSize,那麼必需要知足 lastByteRcv - lastByteRead <= RcvSize 才能保證接收緩存不會溢出,因此咱們定義流量窗口爲接收緩存剩餘的空間,也就是 Rcv = RcvSize - (lastByteRcv - lastByteRead)。只要接收方在響應 ACK 的時候把這個窗口的值帶給發送方,發送方就能知道接收方的接收緩存還有多大的空間,進而設置滑動窗口的大小。
接着講解擁塞控制:
擁塞控制是指發送方先設置一個小的窗口值做爲發送速率,當成功發包並接收到 ACK 時,便以指數速率增大發送窗口的大小,直到遇到丟包(超時/三個冗餘 ACK ),才中止並調整窗口的大小。這麼作能最大限度地利用帶寬,又不至於讓網絡環境變得太過擁擠。
最終滑動窗口的值將設置爲流量控制窗口和擁塞控制窗口中的較小值。
HTTP 和 HTTPS 的主要區別在於 HTTP 協議傳遞的是明文數據,而 HTTPS 傳遞的是加密過的數據,也就是說 HTTPS 更具備安全性。也正由 HTTPS 須要保證安全性,因此它的性能要比 HTTP 差一點。
單說安全性確定是不夠的,我打算擴展講一下 HTTPS 是怎麼解決安全性問題的,經過這些 HTTP 沒有機制,反映出 HTTPS 與 HTTP 的區別。下面嘗試把 HTTPS 加密的過程推導出來。推導過程不涉及複雜的實現細節:
假設如今 A 和 B 要進行安全的通訊,那麼究竟怎樣纔算是安全的通訊?很天然地會想到:A 和 B 之間傳遞數據,這些數據只有 A 和 B 纔看得懂,中間人就算截取了信息但也看不懂,這纔算得上安全。
爲了能讓 A 和 B 才能看懂,就必需要對數據進行加密,並且首先想到的就是對稱加密。對稱加密的意思是 A 和 B 各持有一個相同的密鑰,它們傳遞信息時會用密鑰給信息加密,在消息到達端給消息解密,完成安全通訊。
在對稱加密中又會涉及到加密算法的選擇問題。現實世界中,一般是多個客戶端面向一個服務器的狀況,不可能讓每一個客戶端和服務器之間都採用相同的加密算法,若是是這樣那和沒加密差很少。因此註定每一個客戶端和服務器之間都會採用不一樣的加密方式。
要想對不一樣的機器使用不一樣的加密方式,最直接想到的就是使用隨機數。也就說客戶端和服務器之間每次都基於一個隨機數產生加密算法。(具體實現時爲了保證隨機,用到還不止一個隨機數)
這個產生加密算法的過程稱之爲協商,如今問題是協商的過程是透明的,也就是說中間人能夠截獲協商的過程,從而知道咱們的加密方式。爲了解決這個問題,咱們須要對協商的過程進行加密。
之因此能來到這一步,是由於咱們一開始就選擇使用了對稱加密,也就說一開始的對稱加密緻使瞭如今的問題,因此這時咱們不能再使用對稱加密了,不然會陷入死循環。
在密碼學領域,還有一種加密過程叫非對稱加密,它的邏輯是這樣的:通訊雙方一方持有私鑰,一方持有公鑰,通過私鑰加密的信息,都能經過公鑰進行解密。可是通過公鑰加密的數據,只有私鑰能夠解密。
按照非對稱加密的規則,咱們讓服務器持有私鑰,讓客戶端持有公鑰。這樣就能保證客戶端給服務器發送消息的時候是安全的(相反,服務器給客戶端發送消息就是不安全的),咱們能夠把協商時重要的邏輯安排在客戶端給服務器發送信息的過程當中,從而保證了協商過程的安全性。
如今用非對稱加密算法解決了協商的安全問題,可是非對稱加密的前提是客戶端須要得到公鑰,這又是一個問題了,客戶端與服務器打交道以前是互不知道雙方身份的,怎麼才能讓客戶端得到公鑰呢?
也就只有兩種辦法:
方法2顯然是不行的,尚且不說多了一個訪問節點,如何找到公共服務器的地址也是一個待解決的問題,因此仍是使用方法 1。
可是方法 1 存在一個問題:若是中間人把服務器發送給客戶端的公鑰調包了怎麼辦?也就是說客戶端沒法知道發送公鑰的是不是正真的服務器。
客戶端沒法辨識服務端和中間人的問題稱爲「身份驗證」問題,也就是說咱們須要爲服務器向客戶端發送公鑰的過程進行加密。
這下完了,以前咱們因遇到對稱加密的瓶頸選擇了非對稱加密,如今使用非對稱加密也遇到了瓶頸。顯然這兩種加密方式都是不可用的了,不然會再次陷入死循環。
接下來咱們只好經過第三方機構的介入,解決這個問題。首先咱們本身保存有第三方權威機構的公鑰,而後第三方機構使用私鑰對服務器將要發送給客戶端的公鑰進行加密,客戶端接收到這個經加密的公鑰後(數字證書),就能經過本身保存的第三方機構公鑰進行解密。
「到這裏爲止,咱們解釋了 HTTPS 中使用到的對稱加密,非對稱加密,CA,數字證書的概念,可是還差一個叫數字簽名的概念沒有解釋。
在現實生活中,CA 不單止會給咱們正常公司發放證書,還會給中間人的壞公司發放證書,若是中間人把發放的證書調包了怎麼辦?這時咱們仍能用 CA 的私鑰進行解密,可是證書已經被調包了。
那麼客戶端怎樣驗證證書的真僞呢?答案是證書自己會告訴客戶端如何辨認真僞。比方說證書上面有一個證書編號,還有一個如何計算證書編號的方法,客戶端能夠根據計算證書編號的方法計算出本身要得到的證書的編號,而後把這個編號和證書上的編號進行比對,若是同樣證實沒有被調包。
這裏的證書編號指的就是數字簽名,證書指的就是數字證書。
總結一下 HTTPS :HTTPS 想要保證客戶端與服務器之間的通訊安全,就得使用對稱加密算法進行加密。協商對稱加密算法的過程經過非對稱加密算法來保證。在非對稱加密算法中,客戶端得到公鑰的過程須要第三方機構(CA)經過頒發數字證書保證安全性。
總得來講經過這一系列機制協商出了一個對稱加密算法後,客戶端與服務器之間就能經過該算法進行安全的通訊了。
TCP 是面向鏈接的,使用 TCP 鏈接須要通過三次握手,斷開鏈接須要通過四次揮手。相比於 TCP,UDP 則是面向無鏈接的,它像一個隨時能夠進行通訊的協議,只要接收方建立了 Socket,設置了監聽端口,就能接收到到達的 UDP 包,而且能隨時創建 Socket 進行發包。
TCP 能夠對數據的發送速率做控制,它具備流量控制和擁塞控制機制,經過控制滑動窗口的大小控制數據的發送速率。而 UDP 理論上是能夠無限地向網絡中發數據包的,它的發送速率取決它的應用程序設置。
TCP 保證可靠的數據傳輸,它根據速率控制,重傳控制,還有數據校驗保證提供可靠的數據傳輸服務。而 UDP 只是保證盡力而爲地傳輸數據,並不保證數據的可靠性。
TCP 的報文段頭部佔 20 各字節,比 UDP 的報文段頭部多 12 個字節。消耗的資源更多。因爲創建鏈接有握手的機制,TCP 的使用效率會比 UDP 要低一些。
我的以爲這個問題還能夠擴展一下,試想一下在鍵入 URL 以前,也就是剛開完機的時候,須要聯網,而後才能上網。這個階段包括了獲取本機 IP 地址,獲取 DNS 服務器 IP 地址,得到網關路由器 IP 和 MAC 地址等操做,把這些一塊兒答上去會不會好一些?如下是回答:
首先咱們須要準備一個 DHCP 報文,封裝在一個 UDP 報文段中,裏面包括本機端口號 68 和目的端口號 67,而後到網絡層封裝成數據包裏面包括了本機的初始 IP 0.0.0.0,和廣播地址 255.255.255.255。接着到鏈路層封裝成鏈路層幀。裏面包括廣播地址和本機網卡的 MAC 地址。最後發送到本地局域網中
這個數據包最終會被局域網中的 DHCP 服務器發現(有可能有多個 DHCP 服務器),DHCP 服務器會把可用的 IP 地址返回給咱們的主機。而後操做系統選擇一個 IP 地址併發送給 DHCP 服務器,最後 DHCP 服務器會返回一個包含本機 IP,DNS 服務器 IP,網關路由器 IP 的報文。
接下來咱們須要經過網關路由器的 IP 地址去得到網關路由器的 MAC 地址,這樣咱們才能夠把獲取網站 IP 的 DNS 請求報文由網關發送給 DNS 服務器。這時候咱們須要準備一個 ARP 請求報文,請求獲取網關路由器的 MAC 地址,這個報文一樣是以廣播的方式發送到局域網中,網關路由器接受到請求報文就會把本身的 MAC 地址返回給本機。
接下來一切都準備好了,能夠開始講鍵入 URL 以後的事情了:
首先咱們要訪問 DNS 服務器得到網站對應的 IP 地址,這時咱們須要把 DNS 報文封裝到一個 UDP 報文中,進而封裝到網絡層的數據包中,填上源 IP,目的 DNS 服務器 IP 地址。接着封裝鏈路層,填上網卡 MAC 地址和網關路由器 MAC 地址。接下來這個 DNS 請求報文就會經網關路由器發送給 DNS 服務器。
咱們假設 DNS 服務器緩存有該網站的 IP 地址,(若是沒有緩存會進一步向更高級的DNS服務器索要IP地址)。接着 DNS 服務器會返回該域名的 IP 地址。
拿到了該網站的 IP 地址後就能夠與該網站的服務器創建 TCP 鏈接了。創建 TCP 鏈接須要通過三次握手,過程以下:(更詳細的過程在開頭)
本機的 TCP 首先生成一個不帶任何數據,SYN 標誌位爲 1,序號字段假設爲client_num 的 TCP 報文,通過下層一系列網絡棧後發送給目的 ip 服務器。
該服務器接受到 TCP 請求報文後,會迴應一個贊成鏈接的 TCP 報文,這個報文的 SYN 標誌位也會被置 1,序號字段假設爲 server_num,ACK 響應字段爲 client_num + 1。
接受到贊成鏈接的報文後,我方主機會進行響應,此次的 TCP 報文 SYN 位會被置0,序號字段爲 client_num + 1,ACK 響應字段爲 server + 1。而且此次的響應報文是能夠攜帶數據的。
在三次握手創建鏈接後,本機就能夠向服務器發送 HTTP 請求了,服務器接受到了請求會作出響應的響應,把請求的數據發送給本機瀏覽器,最終瀏覽器把服務器響應的數據渲染顯示出來,咱們就看到了五彩繽紛的網頁。
首先狀態碼的開頭不一樣表明不一樣的類型:
1xx:表明指示信息,表示請求已接收,繼續處理
2xx:表明成功,表示請求已被成功接收,理解,接受
3xx:重定向,表示完成請求必須進行進一步的操做
4xx:客戶端錯誤,請求有語法錯誤或請求沒法實現
5xx:服務器端錯誤,服務器未能實現合法的請求
常見狀態碼:
200 OK:正常返回信息
400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解
403 Forbidden:服務器收到請求,可是拒絕提供服務
404 Not Found:請求資源不存在,輸入了錯誤的URL
500 Internal Server Error:服務器發生不可預期錯誤
503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間後可能恢復正常
從 HTTP 報文層面來看,GET 請求將信息放在 URL,POST 將請求信息放在請求體中。這一點使得 GET 請求攜帶的數據量有限,由於 URL 自己是有長度限制的,而 POST 請求的數據存放在報文體中,所以對大小沒有限制。並且從形式上看,GET 請求把數據放 URL 上感受不太安全,而 POST 請求把數據放在請求體裏彷佛安全一些。實際上想要獲取 POST 請求中的內容仍是很容易的,所以二者在安全性上其實沒有太大差別,想要實現安全的信息傳輸仍是得靠 HTTPS。
從數據庫層面來看,GET 符合冪等性和安全性,而 POST 請求不符合。這個其實和 GET/POST 請求的做用有關。按照 HTTP 的約定,GET 請求用於查看信息,不會改變服務器上的信息;而 POST 請求用來改變服務器上的信息。正由於 GET 請求只查看信息,不改變信息,對數據庫的一次或屢次操做得到的結果是一致的,認爲它符合冪等性。安全性是指對數據庫操做沒有改變數據庫中的數據。
從其餘層面來看,GET 請求可以被緩存,GET 請求可以保存在瀏覽器的瀏覽記錄裏,GET 請求的 URL 可以保存爲瀏覽器書籤。這些都是 POST 請求所不具有的。緩存是 GET 請求被普遍應用的根本,他可以被緩存也是由於它的冪等性和安全性,除了返回結果沒有其餘多餘的動做,所以絕大部分的 GET 請求都被 CDN 緩存起來了,大大減小了 Web 服務器的負擔。
因爲 Http 協議是無狀態協議,若是客戶經過瀏覽器訪問 web 應用時沒有一個保存用戶訪問狀態的機制,那麼將不能持續跟蹤應用的操做。好比當用戶往購物車中添加了商品,web 應用必須在用戶瀏覽別的商品的時候仍保存購物車的狀態,以便用戶繼續往購物車中添加商品。
cookie 是瀏覽器的一種緩存機制,它可用於維持客戶端與服務器端之間的會話。因爲下面一題會講到 session,因此這裏要強調 cookie 會將會話保存在客戶端( session 則是把會話保存在服務端)
這裏以最多見的登錄案例講解cookie的使用過程:
首先用戶在客戶端瀏覽器向服務器發起登錄請求
登錄成功後,服務端會把登錄的用戶信息設置 cookie 中,返回給客戶端瀏覽器
客戶端瀏覽器接收到 cookie 請求後,會把 cookie 保存到本地(多是內存,也多是磁盤,看具體使用狀況而定)
之後再次訪問該 web 應用時,客戶端瀏覽器就會把本地的 cookie 帶上,這樣服務端就能根據 cookie 得到用戶信息了
session 是一種維持客戶端與服務器端會話的機制。可是與 cookie 把會話信息保存在客戶端本地不同,session 把會話保留在瀏覽器端。
咱們一樣以登錄案例爲例子講解 session 的使用過程:
首先用戶在客戶端瀏覽器發起登錄請求
登錄成功後,服務端會把用戶信息保存在服務端,並返回一個惟一的 session 標識給客戶端瀏覽器。
客戶端瀏覽器會把這個惟一的 session 標識保存在起來
之後再次訪問 web 應用時,客戶端瀏覽器會把這個惟一的 session 標識帶上,這樣服務端就能根據這個惟一標識找到用戶信息。
看到這裏可能會引發疑問:把惟一的 session 標識返回給客戶端瀏覽器,而後保存起來,之後訪問時帶上,這難道不是 cookie 嗎?
沒錯,session 只是一種會話機制,在許多 web 應用中,session 機制就是經過 cookie 來實現的。也就是說它只是使用了 cookie 的功能,並非使用 cookie 完成會話保存。與 cookie 在保存客戶端保存會話的機制相反,session 經過 cookie 的功能把會話信息保存到了服務端。
進一步地說,session 是一種維持服務端與客戶端之間會話的機制,它能夠有不一樣的實現。以如今比較流行的小程序爲例,闡述一個 session 的實現方案:
首先用戶登錄後,須要把用戶登錄信息保存在服務端,這裏咱們能夠採用 redis。好比說給用戶生成一個 userToken,而後以 userId 做爲鍵,以 userToken 做爲值保存到 redis 中,並在返回時把 userToken 帶回給小程序端。
小程序端接收到 userToken 後把它緩存起來,之後每當訪問後端服務時就把 userToken 帶上。
在後續的服務中服務端只要拿着小程序端帶來的 userToken 和 redis 中的 userToken 進行比對,就能肯定用戶的登錄狀態了。
通過上面兩道題的闡述,這道題就很清晰了
cookie 是瀏覽器提供的一種緩存機制,它能夠用於維持客戶端與服務端之間的會話
session 指的是維持客戶端與服務端會話的一種機制,它能夠經過 cookie 實現,也能夠經過別的手段實現。
若是用 cookie 實現會話,那麼會話會保存在客戶端瀏覽器中
而 session 機制提供的會話是保存在服務端的。