(2.6w字)網絡知識點靈魂拷問——前端面試必問

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關注公衆號“"},{"type":"text","marks":[{"type":"strong"}],"text":"執鳶者"},{"type":"text","text":"”,獲取大量教學視頻並進入"},{"type":"text","marks":[{"type":"strong"}],"text":"專業交流羣"},{"type":"text","text":"。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9a/9a3d2734b53d10fd2fd73b6e994d989d.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、當瀏覽器輸入一個url請求會經歷什麼?"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/22fdaf7208f00f724ac65248a2156de0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.瀏覽器的地址欄輸入URL並按下回車 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.DNS域名解析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)在瀏覽器DNS緩存中搜索"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)如果瀏覽器緩存中沒有,操作系統會先檢查自己本地的hosts文件是否有這個網址映射關係,如果有,就先調用這個IP地址映射,完成域名解析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)如果hosts裏沒有這個域名的映射,則查找本地DNS解析器緩存,是否有這個網址映射關係,如果有,直接返回,完成域名解析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)如果hosts與本地DNS解析器緩存都沒有相應的網址映射關係,則會找本地DNS服務器,如果要查詢的域名包含在本地配置區域資源中,則返回解析結果給客戶機,完成域名解析,此解析具有權威性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)如果要查詢的域名,不由本地DNS服務器區域解析,但該服務器已緩存了此網址映射關係,則調用這個IP地址映射,完成域名解析,此解析不具有權威性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(6)如果上述方法都失效,由本地DNS服務器進行迭代查詢,先向根域名DNS服務器發出請求,再查二級域、三級域,直到查詢到要解析的地址或名字爲止,本地DNS服務器收到應答後,先在緩存中存儲,然後將解析結果返回客戶機。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.建立TCP連接(三次握手)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP協議採用了三次握手策略。發送端首先發送一個帶SYN(synchronize)標誌的數據包給接收方,接收方收到後,回傳一個帶有SYN/ACK(acknowledegment)標誌的數據包以示傳達確認信息。最後發送方再回傳一個帶ACK標誌的數據包,代表握手結束。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3f/3f8c2e5f6a4f3a7eb9a20c4f370f52a9.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.HTTP發起請求 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完整的HTTP請求包含請求起始行、請求頭部、請求主體三部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GET:獲取資源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"POST:傳輸實體主體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HEAD:獲取報文首部"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PUT:傳輸文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DELETE:刪除文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"OPTIONS:詢問支持的方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TRACE:追蹤路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.服務器處理請求,瀏覽器接收HTTP響應"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務器在收到瀏覽器發送的HTTP請求之後,會將收到的HTTP報文封裝成HTTP的Request對象,並通過不同的Web服務器進行處理,處理完的結果以HTTP的Response對象返回,主要包括狀態碼,響應頭,響應報文三個部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"狀態碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1**:代表請求已經被接收,需要繼續處理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2**:成功狀態碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"200---OK/請求已經正常處理完畢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"204---請求處理成功,但沒有資源返回"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"206---表示客戶端進行了範圍請求,而服務器成功執行了這部分的GET請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3**:重定向狀態碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"301---/請求永久重定向 被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若干個 URI 之一。如果可能,擁有鏈接編輯功能的客戶端應當自動把請求的地址修改爲從服務器反饋回來的地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"302---/請求臨時重定向 由於這樣的重定向是臨時的,客戶端應當繼續向原有地址發送以後的請求。只有在Cache-Control或Expires中進行了指定的情況下,這個響應纔是可緩存的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"304---/表示客戶端發送附帶條件的請求(指採用GET方法的請求報文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)時,服務端允許請求訪問資源,但未滿足條件的情況"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"307---臨時重定向,與302含義相同,但是307會遵照瀏覽器標準,不會從POST變成GET"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4**:客戶端錯誤狀態碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"400---/客戶端請求存在語法錯誤"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"401---/當前請求需要用戶驗證。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"403---/服務器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證並不能提供任何幫助"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"404---/請求失敗,請求所希望得到的資源未被在服務器上發現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"405---/請求行中指定的請求方法不能被用於請求相應的資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5**:服務器錯誤狀態碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"500---/服務器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"501---/服務器不支持當前請求所需要的某個功能。當服務器無法識別請求的方法,並且無法支持其對任何資源的請求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"503---/由於臨時的服務器維護或者過載,服務器當前無法處理請求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"505---/服務器不支持,或者拒絕支持在請求中使用的 HTTP 版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6.瀏覽器解析渲染頁面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)構建文檔對象模型(DOM)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)構建CSS對象模型(CSSOM)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)構建渲染樹(Render Tree)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)佈局"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)繪製"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"7.連接結束(四次揮手)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過四次揮手關閉連接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    第一次揮手是瀏覽器發完數據後,發送FIN請求斷開連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  第二次揮手是服務器發送ACK表示同意。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    第三次揮手是服務器發送FIN請求斷開連接。(考慮到服務器可能還有數據要發送)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  第四次揮手是瀏覽器需要返回ACK表示同意。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/08/0826c95710a9277a96c7a3ffc521caeb.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、HTTP緩存?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP緩存有多種規則,根據是否需要重新向服務器發起請求來分類,將其分爲強制緩存和對比緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)強制緩存:判斷HTTP首部字段:cache-Control、Expires"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/90/907383dfd0f381716a18575a0a19e0d2.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"       Expires是一個絕對時間,即服務器時間。瀏覽器檢查當前時間,如果還沒到失效時間就直接使用緩存文件。但是該方法存在一個問題:服務器時間與客戶端時間可能不一致。因此該字段已經很少使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  cache-control中的max-age保存一個相對時間。例如Cache-Control: max-age = 484200,表示瀏覽器收到文件後,緩存在484200s內均有效。 如果同時存在cache-control和Expires,瀏覽器總是優先使用cache-control。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Cache-Control 是最重要的規則。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常見的取值有private、public、no-cache、max-age,no-store,默認爲private。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"private: 客戶端可以緩存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"public: 客戶端和代理服務器都可緩存(前端的同學,可以認爲public和private是一樣的)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"max-age=xxx: 緩存的內容將在 xxx 秒後失效"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"no-cache: 需要使用對比緩存來驗證緩存數據(後面介紹)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"no-store: 所有內容都不會緩存,強制緩存,對比緩存都不會觸發(對於前端開發)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)對比緩存:通過HTTP的last-modified、Etag字段進行判斷"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c118f6e3b26db70a435096e9aafebe96.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器第一次請求數據時,服務器會將緩存標識與數據一起返回給客戶端,客戶端將二者備份至緩存數據庫中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"last-modified是第一次請求資源時,服務器返回的字段,表示最後一次更新的時間。下一次瀏覽器請求資源時就發送if-modified-since字段。服務器用本地Last-modified時間與if-modified-since時間比較,如果不一致則認爲緩存已過期並返回新資源給瀏覽器;如果時間一致則發送304狀態碼,讓瀏覽器繼續使用緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Etag:資源的實體標識(哈希字符串),當資源內容更新時,Etag會改變。服務器會判斷Etag是否發生變化,如果變化則返回新資源,否則返回304。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/13/13e6b213c33fd6668c9989dc58500b96.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、DNS服務器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.爲什麼需要DNS解析域名爲IP地址"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"網絡通訊大部分是基於TCP/IP的,而TCP/IP是基於IP地址的,所以計算機在網絡上進行通訊時只能識別IP地址而不能認識域名。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.DNS作用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS是域名系統,它所提供的服務是用來將主機名和域名轉換爲IP地址的工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.假設運行在用戶主機上的某些應用程序(如Web瀏覽器或者郵件閱讀器)需要將主機名轉換爲IP地址。這些應用程序將調用DNS的客戶機端,並指明需要被轉換的主機名。用戶主機的DNS客戶端接收到後,向網絡中發送一個DNS查詢報文。所有DNS請求和回答報文使用的UDP數據報經過端口53發送經過若干ms到若干s的延時後,用戶主機上的DNS客戶端接收到一個提供所希望映射的DNS回答報文。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.DNS不採用單點的集中式的設計方式,而是使用分佈式集羣的工作方式,是因爲集中式設計或有單點故障、通信容量、遠距離時間延遲、維護開銷大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.DNS查詢的過程如下圖所示"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7b/7ba58353a28fdc62377ff6eaf065790e.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大致來說,有三種類型的DNS服務器:根DNS服務器、頂級域DNS服務器、權威DNS服務器 "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bd/bd5953a09c7e4e8f39379a3a5bc7127a.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還有另一類重要的DNS,稱爲本地DNS服務器,一臺本地DNS服務器嚴格來說並不屬於該服務器的層次結構,但他對DNS層次結構很重要 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當主機發出DNS請求時,該請求被髮往本地DNS服務器,他起着代理的作用,並將該請求轉發到DNS服務器層次結構中。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5f/5ffa3b12f1dba257b9fe82164cf45bae.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上述中,從請求主機到本地DNS服務器的查詢是遞歸的,其餘查詢是迭代的。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS提供了兩種查詢過程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遞歸查詢:在該模式下DNS服務器接收客戶請求,必須使用一個準確的查詢結果回覆客戶機,如果DNS服務器沒有存儲DNS值,那麼該服務器會詢問其它服務器,並將返回一個查詢結果給客戶機。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"迭代查詢:DNS服務器會向客戶機提供其他能夠解釋查詢請求的DNS服務器,當客戶機發送查詢時DNS並不直接回複查詢結果,而是告訴客戶機,另一臺DNS服務器的地址,客戶機再向這臺DNS服務器提交請求,依次循環直接返回結果。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/49/49c10582b39bd6ed2dbc1fb157fbdede.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、TCP與UDP的區別和優缺點"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.1 TCP與UDP總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)TCP面向連接;UDP是無連接的,即發送數據之前不需要建立連接;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)TCP提供可靠數據傳輸,通過使用流量控制、序號、確認和定時器,TCP確保正確的、按序的將數據從發送進程交付給接收進程;UDP盡最大努力交付,即不保證可靠交付;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)UDP具有較好的實時性,工作效率比TCP高,適用於對高速傳輸和實時性比較高的通信或廣播通信;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)每一條TCP連接只能是點對點的,UDP支持一對一,一對多,多對一和多對多的交互通信"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)TCP對系統資源要求較多,UDP對系統資源要求較少。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.2 UDP應用場景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)面向數據報方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)網絡數據大多爲短消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)擁有大量Client"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)對數據安全性無特殊要求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)網絡負擔非常重,但對響應速度要求高"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.3 TCP如何提供可靠數據傳輸"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過使用流量控制、序號、確認和定時器,TCP確保正確的、按序的將數據從發送進程交付給接收進程。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"五、TCP發送方有三個與發送和重傳有關的事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從上層應用程序接收數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP從應用程序接收數據,將數據封裝在一個報文段中(含有第一個數據字節的流編號),然後交給IP。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定時器超時"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超時後,TCP重傳超時報文,然後,重啓定時器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"收到ACK"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"收到ACK後,將確認報文中確認號與發送方的SendBase(最早未被確認的字節序號)比較。TCP採取累積確認,所以確認號之前的字節都被接收方收到。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當 確認號 > SendBase 時,則該ACK是在確認一個或多個先前未被確認的報文段,此時發送方更新SendBase的值"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果當前有未被確認的報文段,TCP重啓定時器"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"六、TCP協議在工作過程中的幾種簡單情況"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.1 由於確認丟失而重傳      "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cb/cbdaa88b7df03b88bc820638276ecb45.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  如上圖所示,B發送給A的ACK丟失,引起了主機A的重傳,B在接收到重傳數據報後根據序號得知這是重傳報文,於是丟棄該報文,向A發送ACK。 "}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.2 連續發送的報文段的ACK延遲    "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e2/e239a5b6129b16920130da068f89bbdc.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  A連續向B發送了兩個報文段,但是他們的ACK都延遲了,導致定時器超時,於是最早的未被確認的報文段92被重傳,接着他們的ACK到達,它們就不會被再次重傳,A收到確認後,就會將SendBase後移,並重啓定時器。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.3 累積確認避免先前報文段重傳     "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/14/1425a5ec021f54ba8f7087f1c6560c4a.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  A還是向B連續發送了兩個報文段,但是第一個報文段的ACK丟失啦。但是好的是在定時器超時之前,第二個報文段的ACK到達,因爲TCP採取了累計確認,第二個報文段ACK到達,說明了第一個報文段是被正確接收了噠。所以第一個報文段不會被重傳。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"七、快速重傳"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超時重傳存在的問題之一就是超時週期可能較長。當一個報文段丟失時,通過超時重傳來恢復報文,就會增加端到端的時延。Luckily,可以通過檢測收到的冗餘ACK來進行對丟失報文段的重傳。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至於爲啥可以通過這樣的方式來確信此報文段丟失是因爲:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  ①接送方接到丟失報文段後的報文(也就是失序報文段)會將失序報文段緩存,並向發送方發送最近接收的未失序報文段的最大編號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  ②如果接收方連續接收多個失序報文,那麼發送方將會收到對一個報文段的多個ACK,由此發送方可知該ACK代表的報文段的後一個報文丟失了,於是,發送方重傳丟失報文。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  當發送方收到3個冗餘ACK,就說明被確認過三次的報文段之後的那個報文段已經丟失,TCP就執行快重傳(fast retransmit),在丟失報文段定時器超時之前重傳丟失報文段。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/072891216550919499e0c64c744e2805.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"八、TCP中是回退N步還是選擇重傳"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據前面對TCP描述,可以得知TCP確認是採用累積確認方式,並且對失序報文不會給出確認。這讓TCP看起來像是一個GBN協議,但是與GBN不同的是,TCP會緩存失序的分組。所以,TCP提出的一種修改意見是選擇確認(slective acknowledgment)[RFC 2018],它允許TCP接收方有選擇地確認失序報文段,而不是累積確認最後一個正確接收的有序報文段。當將該機制和選擇重傳機制結合起來使用時(即跳過重傳那些已被接收方選擇確認過的報文段),TCP就像我們通常的SR協議。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  因此,TCP的差錯恢復機制爲GBN協議和SR協議的混合體。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"九、TCP中流量控制與擁塞控制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他們都是對發送方的遏制"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9.1 流量控制(爲了避免接收方緩存溢出問題)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.爲什麼要提供流量控制服務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單地說,提供流控就是爲了避免接收方緩存溢出問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  接收方接收到數據後,會將其放入接收緩存中,待上層應用程序讀取數據。但是上層應用可能忙於其他事務或者讀取數據的速度比較慢,而發送方發送數據的太多,速率太快,此時就會導致接收方的緩存溢出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  流量控制也是一個速率匹配服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.TCP如何提供流量控制服務?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏爲了從整體上看問題,我們假設,TCP接收方會丟棄失序的報文。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP讓發送方A維護一個稱爲接收窗口(receive window)的變量來提供流量控制。這個窗口代表接收方B有多少可用的緩存空間"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主機A和主機B之間建立TCP連接後,主機B爲連接分配了一個接收緩存,用RcvBuffer表示"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定義如下變量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LastByteRead:主機B的應用進程從緩存中取出的數據流最後一個字節的編號"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LastByteRevd:主機B緩存的數據流的最後一個字節編號"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"緩存不能溢出需滿足"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LastByteRevd - LastByteRead <= RevBuffer"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收窗口rwnd根據緩存可用空間設置:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rwnd = RevBuffer - [LastByteRevd-LastByteRead]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主機B通過把當前的rwnd放到它發送給主機A的報文段的接收窗口字段,已通知主機A當前它還有多少空間可用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主機A始終跟蹤兩個LastByteSend和LastByteAcked,[LastByteSend-LastByteAcked]就是主機A中發送但未被確認的數據量。使這個值小於主機B的rwnd,就可以使主機B的緩存不會溢出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9.2 擁塞控制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP的發送方也可能會因爲IP網絡擁塞而被遏制,這種形式的控制被稱爲擁塞控制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般原理:發生擁塞控制的原因:資源(帶寬、交換節點的緩存、處理機)的需求>可用資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.擁塞的標誌"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"重傳計時器超時或接收到三個重複確認。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.慢啓動與擁賽避免"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)慢啓動不是指cwnd(擁塞窗口)的增長速度慢(指數增長),而是指TCP開始發送設置cwnd=1。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)思路:不要一開始就發送大量的數據,先探測一下網絡的擁塞程度,也就是說由小到大逐漸增加擁塞窗口的大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)爲了防止cwnd增長過大引起網絡擁塞,設置一個慢啓動閾值"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當cnwd<ssthresh,使用慢開始算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當cnwd=ssthresh,既可使用慢開始算法,也可以使用擁塞避免算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當cnwd>ssthresh,使用擁塞避免算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.擁塞避免"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)擁塞避免並非完全能夠避免擁塞,是說在擁塞避免階段將擁塞窗口控制爲按線性規律增長,使網絡比較不容易出現擁塞。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)思路:讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞控制窗口加一。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論是在慢開始階段還是在擁塞避免階段,只要發送方判斷網絡出現擁塞,就把慢開始門限設置爲出現擁塞時的發送窗口大小的一半。然後把擁塞窗口設置爲1,執行慢啓動算法。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f4/f4e327ddb7538165227b8f1164fd4e1d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.快重傳與快恢復 "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d20963366a81a52db3caa144940952e1.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"快重傳 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)快重傳要求接收方在收到一個失序的報文段後就立即發出重複確認(爲的是使發送方及早知道有報文段沒有到達對方)而不要等到自己發送數據時捎帶確認。快重傳算法規定,發送方只要一連收到三個重複確認就應當立即重傳對方尚未收到的報文段,而不必繼續等待設置的重傳計時器時間到期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)由於不需要等待設置的重傳計時器到期,能儘早重傳未被確認的報文段,能提高整個網絡的吞吐量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"快恢復(與快重傳配合使用)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)採用快恢復算法時,慢啓動只在TCP連接建立時和網絡出現超時時才使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)當發送方連續收到三個重複確認時,就執行“乘法減小”算法,把ssthresh門限減半。但是接下去並不執行慢啓動算法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)考慮到如果網絡出現擁塞的話就不會收到好幾個重複的確認,所以發送方現在認爲網絡可能沒有出現擁塞。所以此時不執行慢啓動算法,而是將cwnd設置爲ssthresh的大小,然後執行擁塞避免算法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.擁塞窗口"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送方爲一個動態變化的窗口叫做擁塞窗口,擁塞窗口的大小取決於網絡的擁塞程度。發送方讓自己的發送窗口=擁塞窗口,但是發送窗口不是一直等於擁塞窗口的,在網絡情況好的時候,擁塞窗口不斷的增加,發送方的窗口自然也隨着增加,但是接受方的接受能力有限,在發送方的窗口達到某個大小時就不在發生變化了。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9.3 擁塞控制和流量控制的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"擁塞控制是防止過多的數據注入到網絡中,可以使網絡中的路由器或鏈路不致過載,是一個全局性的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"流量控制是點對點通信量的控制,是一個端到端的問題,主要就是抑制發送端發送數據的速率,以便接收端來得及接收。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十、TCP三次握手和四次揮手全過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"確認ACK,僅當ACK=1時,確認號字段纔有效。TCP規定,在連接建立後所有報文的傳輸都必須把ACK置1;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步SYN,在連接建立時用來同步序號。當SYN=1,ACK=0,表明是連接請求報文,若同意連接,則響應報文中應該使SYN=1,ACK=1;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"終止FIN,用來釋放連接。當FIN=1,表明此報文的發送方的數據已經發送完畢,並且要求釋放;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10.1 三次握手"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最開始的時候客戶端和服務器都是處於CLOSED狀態。主動打開連接的爲客戶端,被動打開連接的是服務器。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/58/585b5237d13c77c331d98583c6fa6885.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.TCP服務器進程先創建傳輸控制塊TCB(線程控制塊),時刻準備接受客戶進程的連接請求,此時服務器就進入了LISTEN(監聽)狀態; "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.TCP客戶進程也是先創建傳輸控制塊TCB,然後向服務器發出連接請求報文,這時報文首部中的同部位SYN=1,同時選擇一個初始序列號 seq=x ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.TCP服務器收到請求報文後,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=x+1,同時也要爲自己初始化一個序列號 seq=y,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.TCP客戶進程收到確認後,還要向服務器給出確認。確認報文的ACK=1,ack=y+1,自己的序列號seq=x+1,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.當服務器收到客戶端的確認後也進入ESTABLISHED狀態,此後雙方就可以開始通信了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"擴展:爲什麼TCP客戶端最後還要發送一次確認呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一句話,主要防止已經失效的連接請求報文突然又傳送到了服務器,從而產生錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用的是兩次握手建立連接,假設有這樣一種場景,客戶端發送了第一個請求連接並且沒有丟失,只是因爲在網絡結點中滯留的時間太長了,由於TCP的客戶端遲遲沒有收到確認報文,以爲服務器沒有收到,此時重新向服務器發送這條報文,此後客戶端和服務器經過兩次握手完成連接,傳輸數據,然後關閉連接。此時此前滯留的那一次請求連接,網絡通暢了到達了服務器,這個報文本該是失效的,但是,兩次握手的機制將會讓客戶端和服務器再次建立連接,這將導致不必要的錯誤和資源的浪費。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果採用的是三次握手,就算是那一次失效的報文傳送過來了,服務端接受到了那條失效報文並且回覆了確認報文,但是客戶端不會再次發出確認。由於服務器收不到確認,就知道客戶端並沒有請求連接。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10.2 四次揮手"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/1764d1d1ff151bf8d283903f4d33b85a.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.客戶端收到服務器的連接釋放報文後,必鬚髮出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6.服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"擴展:爲什麼客戶端最後要等待2MSL?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MSL(Maximum Segment Lifetime),TCP允許不同的實現可以設置不同的MSL值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一,保證客戶端發送的最後一個ACK報文能夠到達服務器,因爲這個ACK報文可能丟失,站在服務器的角度看來,我已經發送了FIN+ACK報文請求斷開了,客戶端還沒有給我回應,應該是我發送的請求斷開報文它沒有收到,於是服務器又會重新發送一次,而客戶端就能在這個2MSL時間段內收到這個重傳的報文,接着給出迴應報文,並且會重啓2MSL計時器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二,防止類似與“三次握手”中提到了的“已經失效的連接請求報文段”出現在本連接中。客戶端發送完最後一個確認報文後,在這個2MSL時間中,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣新的連接中不會出現舊連接的請求報文。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10.3 爲什麼建立連接是三次握手,關閉連接是四次揮手呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"建立連接的時候, 服務器在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而關閉連接時,服務器收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,而自己也未必全部數據都發送給對方了,所以己方可以立即關閉,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送,從而導致多了一次。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10.4 如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75分鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十一、HTTP協議(請求報文和響應報文)"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a8b8d9b4bcda9eb37aeccea17271b532.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"11.1 請求報文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP請求報文主要包括:請求行、請求頭部以及請求的數據(實體)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.請求行:方法字段、URI字段和協議版本"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方法字段:GET(請求獲取內容)、POST(提交表單)、HEAD(請求資源響應消息報頭)、PUT(傳輸文件)、DELETE(請求刪除URI指向的資源)、OPTIONS(查詢針對請求URI指定的資源支持的方法)、TRACE(追蹤請求經過路徑)、CONNECT(要求用隧道協議連接代理)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.請求頭部"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常見標頭有:Connection標頭(連接管理)、Host標頭(指定請求資源的主機)、Range標頭(請求實體的字節範圍)、User-Agent標頭(包含發出請求的用戶信息)、Accept標頭(首選的媒體類型)、Accept-Language(首選的自然語言)"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/44/44d5659420de55b440735a5335a43b04.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.HTTP請求的body主要用於提交表單場景。實際上,http請求的bodt是比較自由的,只要瀏覽器端發送的body服務端認可就可以了。一些常見的body格式是: "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"application/json"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"application/x-www-form-urlencoded"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"multipart/form-data"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"text/xml"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用html的form標籤提交產生的html請求,默認會產生 application/x-www-form-urlencoded 的數據格式,當有文件上傳時,則會使用multipart/form-data。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"11.2 響應報文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP響應報文分爲三個部分:狀態行、首部行和實體;
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.狀態行:版本、狀態碼和原因語句
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"狀態碼:
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)1xx:這一類型的狀態碼,代表請求已被接受,需要繼續處理。這類響應是臨時響應,只包含狀態行和某些可選的響應頭信息,並以空行結束;
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)2xx:這一類型的狀態碼,代表請求已成功被服務器接收、理解並接受;
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)3xx:這類狀態碼代表需要客戶端採取進一步的操作才能完成請求。通常,這些狀態碼用來重定向,後續的請求地址(重定向目標)在本次響應的Location域中指明。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)4xx:這類狀態碼代表客戶端類的錯誤;
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)5xx:服務器類的錯誤。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"200---OK/請求已經正常處理完畢
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"204---請求處理成功,但沒有資源返回
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"206---表示客戶端進行了範圍請求,而服務器成功執行了這部分的GET請求
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"301---/請求永久重定向 被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若干個 URI 之一。如果可能,擁有鏈接編輯功能的客戶端應當自動把請求的地址修改爲從服務器反饋回來的地址。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"302---/請求臨時重定向 由於這樣的重定向是臨時的,客戶端應當繼續向原有地址發送以後的請求。只有在Cache-Control或Expires中進行了指定的情況下,這個響應纔是可緩存的。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"303---表示由於請求對應的資源存在着另一個URI,應使用GET方法定向獲取請求的資源
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"304---/表示客戶端發送附帶條件的請求(指採用GET方法的請求報文中包含If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since中任一首部)時,服務端允許請求訪問資源,但未滿足條件的情況
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"307---臨時重定向,與302含義相同,但是307會遵照瀏覽器標準,不會從POST變成GET
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"400---/客戶端請求存在語法錯誤
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"401---/當前請求需要用戶驗證。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"403---/服務器已經理解請求,但是拒絕執行它。與401響應不同的是,身份驗證並不能提供任何幫助
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"404---/請求失敗,請求所希望得到的資源未被在服務器上發現。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"405---/請求行中指定的請求方法不能被用於請求相應的資源。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"500---/服務器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"501---/服務器不支持當前請求所需要的某個功能。當服務器無法識別請求的方法,並且無法支持其對任何資源的請求。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"503---/由於臨時的服務器維護或者過載,服務器當前無法處理請求。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"505---/服務器不支持,或者拒絕支持在請求中使用的 HTTP 版本。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.響應首部"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Date(響應的時間)、Via(報文經過的中間節點)、Last-Modified(上一次修改時間)、Etag(與此實體相關的實體標記)、Connection(連接狀態)、Accept-Ranges(服務器可接收的範圍類型)、Content-Type(資源類型)"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7c1a30e371cc29e40a924bfc0a8a6406.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十二、Cookie和Session"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"12.1 Cookie 大小4KB"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Cookie的工作機制是用戶識別及狀態管理。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0d/0d5ed47368ef1235df7392dd4bc7616b.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.Set-Cookie"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該首部字段用來告知客戶端有關Cookie的各種信息"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/19/19551120cb632f4cffe9ade7329297fa.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.Cookie "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該首部字段用來告知服務器,當客戶端想獲得HTTP狀態管理支持時,就會在請求中包含從服務器接收到的Cookie。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.HTTP是無狀態的協議,即HTTP協議自身不對請求和響應之間的通信狀態進行保存。但爲了實現期望的保持狀態功能,於是引入了Cookie技術。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Cookie會根據從服務端發送的響應報文內的一個叫做Set-Cookie的首部字段信息,通知客戶端保存Cookie。當下次客戶端再往該服務器發送請求時,客戶端會自動在請求報文中加入Cookie值發送出去。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cb/cbb09ef01f01cf63e504daa8df8dad5c.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.expires/Max-Age 字段爲此cookie超時時間。若設置其值爲一個時間,那麼當到達此時間後,此cookie失效。不設置的話默認值是Session,意思是cookie會和session一起失效。當瀏覽器關閉(不是瀏覽器標籤頁,而是整個瀏覽器) 後,此cookie失效。 "}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"12.2 Session"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Session是另一種記錄客戶狀態的機制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session。客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.後端怎麼存儲session?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在服務器內存中開闢一塊地址空間,專門存放每個客戶端的私有數據,每個客戶端根據cookie中保持的sessionId,可以獲取到session數據。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"12.3 Cookie與Session區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)、Cookie和Session都是會話技術,Cookie是運行在客戶端,Session是運行在服務器端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)、Cookie有大小限制4K以及瀏覽器在存cookie的個數也有限制,Session是沒有大小限制和服務器的內存大小有關。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3)、Cookie有安全隱患,通過攔截或本地文件找得到你的cookie後可以進行攻擊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4)、Session是保存在服務器端上會存在一段時間纔會消失,如果session過多會增加服務器的壓力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"session用於保存重要的信息,cookie用於保存不重要的用戶信息"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十三、登陸驗證"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於HTTP是無狀態的,一次請求結束,連接斷開,下次服務器再收到請求,它就不知道這個請求是哪個用戶發過來的。所以需要狀態管理,以便服務端能夠準確的知道http請求是哪個用戶發起的,從而判斷用戶是否有權限繼續這個請求。這個過程就是常說的會話管理。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"13.1 同域登陸"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果前端,後臺API部署在同域下,不存在跨域的情況,登錄方式相對簡單"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.基於Session登錄"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務器端使用Session技術,瀏覽器端使用Cookie技術。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bb09aa69b6c5a6ce66b23d27859b3589.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"session在一開始並不具備會話管理的作用。它只有在用戶登錄認證成功之後,並且往sesssion對象裏面放入了用戶登錄成功的憑證,才能用來管理會話。管理會話的邏輯也很簡單,只要拿到用戶的session對象,看它裏面有沒有登錄成功的憑證,就能判斷這個用戶是否已經登錄。當用戶主動退出的時候,會把它的session對象裏的登錄憑證清掉。所以在用戶登錄前或退出後或者session對象失效時,肯定都是拿不到需要的登錄憑證的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"其實上述可以解釋爲:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"登錄時帶着自身的用戶名和密碼,將用戶登錄成功的憑證信息存儲在Session中,並生成Cookie,並通過Set-Cookie在響應中返回;第二次登陸時,在請求中添加Cookie後發送,服務端檢查Cookie,然後進行響應。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.基於Token登錄"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/17ec7e916d5936e060a9a066f16ff705.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1).用戶在瀏覽器中輸入用戶和密碼,後臺服務器通過加密或者其他邏輯,生成一個Token。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2).前端獲取到Token,存儲到cookie或者localStorage中,在接下來的請求中,將token通過url參數或者HTTP Header頭部傳入到服務器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3).服務器獲取token值,通過查找數據庫判斷當前token是否有效"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"13.2 跨域登陸"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於瀏覽器同源策略,凡是發送請求的url的協議、域名和端口號三者之間任意一個與當前頁面不同則視爲跨域"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.解決同源策略"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於Session和Token登錄都要解決"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過在服務端設置Header,設置Access-Control-Allow-Origin"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果要發送Cookie,Access-Control-Allow-Origin就不能設置星號,必須指定明確的、與請求網頁一致的域名。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.解決請求帶上Cookie信息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CORS默認不發送Cookie和HTTP認證信息,一方面服務器制定Access-Control-Allow-Credentials:true"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一方面開發者必須在Ajax請求中添加withCredentials屬性。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十四、HTTP1.0、HTTP1.1、HTTP2.0"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14.1 HTTP的基本優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"影響HTTP網絡請求的因素主要有兩個:帶寬和延遲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.帶寬"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"已經得到極大提升"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.延遲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)瀏覽器阻塞:瀏覽器對於同一個域名,同時只能有6個連接(不同瀏覽器不同),超過瀏覽器最大連接數限制,後續請求就會被阻塞。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)DNS查詢:瀏覽器需要知道目標服務器的IP才能建立連接,通常可以利用DNS緩存結果來達到減少這個時間的目的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)建立連接:HTTP 是基於 TCP 協議的,瀏覽器最快也要在第三次握手時才能捎帶 HTTP 請求報文,達到真正的建立連接,但是這些連接無法複用會導致每次請求都經歷三次握手和慢啓動。三次握手在高延遲的場景下影響較明顯,慢啓動則對文件類大請求影響較大。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14.2 HTTP1.0和HTTP1.1的一些區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.緩存處理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在HTTP1.0中主要使用header裏的If-Modified-Since,Expires來做爲緩存判斷的標準,HTTP1.1則引入了更多的緩存控制策略例如Etag,If-Unmodified-Since, If-Match, If-None-Match等更多可供選擇的緩存頭來控制緩存策略。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.帶寬優化及網絡連接的使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP1.0中,存在一些浪費帶寬的現象,例如客戶端只是需要某個對象的一部分,而服務器卻將整個對象送過來了,並且不支持斷點續傳功能,HTTP1.1則在請求頭引入了range頭域,它允許只請求資源的某個部分,即返回碼是206(Partial Content),這樣就方便了開發者自由的選擇以便於充分利用帶寬和連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.錯誤通知的管理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在HTTP1.1中新增了24個錯誤狀態響應碼,如409(Conflict)表示請求的資源與資源的當前狀態發生衝突;410(Gone)表示服務器上的某個資源被永久性的刪除。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.Host頭處理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在HTTP1.0中認爲每臺服務器都綁定一個唯一的IP地址,因此,請求消息中的URL並沒有傳遞主機名(hostname)。但隨着虛擬主機技術的發展,在一臺物理服務器上可以存在多個虛擬主機(Multi-homed Web Servers),並且它們共享一個IP地址。HTTP1.1的請求消息和響應消息都應支持Host頭域,且請求消息中如果沒有Host頭域會報告一個錯誤(400 Bad Request)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.長連接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 1.1支持長連接(PersistentConnection)和管線化處理,在一個TCP連接上可以傳送多個HTTP請求和響應,減少了建立和關閉連接的消耗和延遲,在HTTP1.1中默認開啓Connection: keep-alive,一定程度上彌補了HTTP1.0每次請求都要創建連接的缺點。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14.3 擴展:HTTP1.1的幾個特點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.持久連接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個TCP連接開始都有三次握手,要經歷一次客戶端與服務器間完整的往返,而開啓了持久化連接就能不必每次都要握手。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持久化連接Connection:keep-alive"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3a/3a9392eee1fef902c63e7604db2c8f66.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.HTTP管道 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持久HTTP多次請求必須嚴格滿足先進先出(FIFO)的隊列順序:發送請求,等待響應完成,再發送客戶端隊列中的下一個請求。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP管道可以讓我們把FIFO隊列從客戶端(請求隊列)遷移到服務器(響應隊列)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但HTTP 1.x不允許一個連接上的多個響應數據交錯到達(多路複用),因而一個響應必須完全返回後,下一個響應纔會開始傳輸。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/63/63aa43fdd612d4c54ab1818dcdef83d9.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14.4 HTTP的瓶頸"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一條連接上只可發送一個請求;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求只能從客戶端開始。客戶端不可以接收除響應以外的指令;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求/響應首部未經壓縮就發送。首部信息越多延遲越大;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送冗長的首部。每次互相發送相同的首部造成的浪費較多;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可任意選擇數據壓縮格式。非強制壓縮發送;"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14.5 HTTP2.0"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP2.0是在SPDY基礎上形成的下一代互聯網通信協議。HTTP/2的目的是通過支持請求和響應的多路複用來減少延遲,通過壓縮HTTP首部字段將協議開銷降低,同時增加請求優先級和服務端推送的支持。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一、HTTP2.0相對於HTTP1.X的新特性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.二進制分幀層"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二進制分幀層是HTTP2.0性能增強的核心"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 1.x在應用層以純文本的形式進行通信,而HTTP 2.0將所有的傳輸信息分割爲更小的消息和幀,並對它們採用二進制格式編碼。這樣,客戶端和服務端都需要引入新的二進制編碼和解碼的機制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)幀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP2.0通信的最小單位,包括幀首部、流標識符、優先值和幀淨荷等。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/62f1f44d2b4dfc236ee219cc0742edfc.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,幀類型又可以分爲: "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DATA:用於傳輸HTTP消息體;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HEADERS:用於傳輸首部字段;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SETTINGS:用於約定客戶端和服務端的配置數據。比如設置初識的雙向流量控制窗口大小;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WINDOW_UPDATE:用於調整個別流或個別連接的流量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PRIORITY: 用於指定或重新指定引用資源的優先級。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RST_STREAM: 用於通知流的非正常終止。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PUSH_ PROMISE: 服務端推送許可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PING: 用於計算往返時間,執行“ 活性” 檢活。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GOAWAY: 用於通知對端停止在當前連接中創建流。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"消息是指邏輯上的HTTP消息(請求/響應)。一系列數據幀組成了一個完整的消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)流"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"流是連接中的一個虛擬信道,可以承載雙向消息傳輸。每個流有唯一整數標識符。爲了防止兩端流ID衝突,客戶端發起的流具有奇數ID,服務端發起的流具有偶數ID。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有HTTP2.0通信都在一個TCP連接上完成,這個連接可以承載任意數量的雙向數據流Stream。相應地,每個數據流以消息的形式發送,而消息由一個或多個幀組成,這些幀可以亂序發送,然後根據每個幀首部的流標識符重新組裝。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.多路複用共享連接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP消息被分解爲獨立的幀,而不破壞消息本身的語義,交錯發送出去,最後在另一端根據流ID和首部將它們重新組合起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP2.0成功解決了HTTP1.x的隊首阻塞問題(TCP層的阻塞仍無法解決),同時減少了TCP連接數對服務器性能有很大提升。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以並行交錯地發送請求,請求之間互不影響; "},{"type":"text","marks":[{"type":"italic"}],"text":" 可以並行交錯地發送響應,響應之間互不干擾; "},{"type":"text","text":" 只使用一個連接即可並行發送多個請求和響應; "},{"type":"text","marks":[{"type":"italic"}],"text":" 消除不必要的延遲,從而減少頁面加載的時間; "},{"type":"text","text":" 不必再爲繞過 HTTP 1.x 限制而多做很多工作;"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.請求優先級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"流可以帶有一個31bit的優先級:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"0:表示最高優先級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2的31次方-1 表示最低優先級。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務端按優先級返回結果有利於高效利用底層連接,提高用戶體驗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.服務端推送"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP2.0增加了服務端推送功能,服務端可以根據客戶端的請求,提前返回多個響應,推送額外的資源給客戶端。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ad/ad69c7228a040739c2b772c8db79c56a.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 2.0 連接後,客戶端與服務器交換SETTINGS 幀,藉此可以限定雙向併發的流的最大數量。因此,客戶端可以限定推送流的數量,或者通過把這個值設置爲0 而完全禁用服務器推送。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有推送的資源都遵守同源策略。換句話說,服務器不能隨便將第三方資源推送給客戶端,而必須是經過雙方確認纔行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"PUSH_PROMISE幀是服務端向客戶端有意推送資源的信號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果客戶端不需要服務端Push,可在SETTINGS幀中設定服務端流的值爲0,禁用此功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PUSH"},{"type":"text","marks":[{"type":"italic"}],"text":"PROMISE幀中只包含預推送資源的首部。如果客戶端對PUSH"},{"type":"text","text":"PROMISE幀沒有意見,服務端在PUSH"},{"type":"text","marks":[{"type":"italic"}],"text":"PROMISE幀後發送響應的DATA幀開始推送資源。如果客戶端已經緩存該資源,不需要再推送,可以選擇拒絕PUSH"},{"type":"text","text":"PROMISE幀。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PUSH_PROMISE必須遵循請求-響應原則,只能藉着對請求的響應推送資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"幾點限制:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務器必須遵循請求- 響應的循環,只能藉着對請求的響應推送資源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PUSH_PROMISE 幀必須在返回響應之前發送,以免客戶端出現競態條件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.首部壓縮"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP1.x每一次通信(請求/響應)都會攜帶首部信息用於描述資源屬性。HTTP2.0在客戶端和服務端之間使用“首部表”來跟蹤和存儲之前發送的鍵值對.首部表在連接過程中始終存在,新增的鍵值對會更新到表尾,因此,不需要每次通信都需要再攜帶首部。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP2.0使用了首部壓縮技術,可讓報頭更緊湊、更快速傳輸。HTTP 2.0關注的是首部壓縮,而我們常用的gzip等是報文內容(body)的壓縮。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二、HTTP2.0的完整通信過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在兩端使用HTTP2.0通信之前,必然存在協議協商的過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.基於ALPN的協商過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTPS 協商過程中有一個環節會使用ALPN(應用層協議協商)。減少網絡延遲是HTTP 2.0 的關鍵條件,因此在建立HTTPS 連接時一定會用到ALPN協商。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持HTTP 2.0的瀏覽器可以在TLS會話層自發完成和服務端的協議協商以確定是否使用HTTP 2.0通信。其原理是TLS 1.2中引入了擴展字段,以允許協議的擴展,其中ALPN協議(Application Layer Protocol Negotiation, 應用層協議協商, 前身是NPN)用於客戶端和服務端的協議協商過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.基於HTTP的協商過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端使用HTTP也可以開啓HTTP 2.0通信。只不過因爲HTTP 1\\. 0和HTTP 2\\. 0都使用同一個 端口(80), 又沒有服務器是否支持HTTP 2\\. 0的其他任何 信息,此時 客戶端只能使用HTTP Upgrade機制(OkHttp, nghttp2等組件均可實現,也可以自己編碼完成)通過協調確定適當的協議"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.完整通信過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)TCP連接建立"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)TLS握手和HTTP2.0通信過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)TLS握手"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)客戶端向服務端發送SETTINGS幀,約定配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3)流量控制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4)客戶端向服務端發送HEADERS幀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5)服務端返回配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6)DATA幀傳輸數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"三、發起新流"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在發送應用數據之前,必須創建一個新流並隨之發送相應的元數據,比如流優先級、HTTP 首部等;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端通過發送HEADERS幀來發起新流;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務器通過發送 PUSH_PROMISE 幀來發起推送流。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"四、發送應用數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建新流併發送HTTP 首部之後,接下來就是利用DATA 幀。應用數據可以分爲多個DATA 幀,最後一幀要翻轉幀首部的END_STREAM 字段"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 2.0 標準要求DATA 幀不能超過2的14次方-1(16383)字節。長度超過這個閥值的數據,就得分幀發送。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"五、去掉對HTTP1.X的優化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個來源使用一個連接,HTTP 2.0 通過將一個TCP 連接的吞吐量最大化來提升性能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"去掉不必要的文件合併和圖片拼接:HTTP 2.0,很多小資源都可以並行發送"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用服務器推送:之前針對HTTP 1.x 而嵌入的大多數資源,都可以而且應該通過服務器推送來交付。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十五、HTTPS"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.1 HTTP的缺點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1.通信使用明文(不加密),內容可能會被竊聽;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP/IP是可能被竊聽的網絡:按TCP/IP協議族的工作機制,通信內容在所有線路上都有可能遭到窺視。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加密處理防止被竊聽,加密的對象如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)通信的加密"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過和SSL(Secure Socket Layer,安全套接層)或TLS(Transport Layer Security,安全層傳輸協議)的組合使用,加密HTTP的通信內容。用SSL建立安全通信線路之後,就可以在這條線路上進行HTTP通信了。與SSL組合使用的HTTP稱爲HTTPS。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種方式將整個通信線路加密"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)內容的加密"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於HTTP協議中沒有加密機制,那麼就對HTTP協議傳輸的內容本身加密。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了做到有效的內容加密,前提是要求客戶端和服務器同時具備加密和解密機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該方式不同於將整個通信線路加密處理,內容仍有被篡改的風險。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2.不驗證通信方的身份,因此有可能遭遇僞裝;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)任何人都可發起請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP協議不論是誰發送過來的請求都會返回響應,因此不確認通信方,會存在以下隱患:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法確定請求發送至目標的Web服務器是否是按真是意圖返回響應的那臺服務器,有可能是已僞裝的Web服務器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法確定響應返回到的客戶端是否是按照真實意圖接收響應的那個客戶端,有可能是僞裝的客戶端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法確定正在通信的對方是否具備訪問權限,因爲某些Web服務器上保存着重要的信息,只想發給特定用戶通信的權限。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法判斷請求是來自何方、出自誰手"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使是無意義的請求也會照單全收,無法阻止海量請求下的Dos攻擊(Denial of Service,拒絕服務攻擊)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)查明對手的證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SSL不僅提供加密處理,還使用了一種被稱爲證書的手段,可用於確定通信方。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書由值得信任的第三方機構頒發,用以證明服務器和客戶端是實際存在的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用證書,以證明通信方就是意料中的服務器,對使用者個人來講,也減少了個人信息泄露的危險性。另外,客戶端持有證書即可完成個人身份的確認,也可用於對Web網站的認證環節。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"3.無法證明報文的完整性,所以有可能已遭遇篡改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)接收到的內容可能有誤"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在請求或響應送出之後直到對方接收之前的這段時間內,即使請求或響應的內容遭到篡改,也沒有辦法獲悉。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在請求或響應的傳輸途中,遭攻擊者攔截並篡改內容的攻擊稱爲中間人攻擊(Man-in-the-Middle attack)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)如何防止篡改"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僅靠HTTP確保完整性是非常困難的,便有賴於HTTPS來實現。SSL提供認證和加密處理及摘要功能。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.2 HTTPS是如何加密數據的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把添加了加密、認證機制、完整性保護的HTTP稱爲HTTPS"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c8/c89e37ee1bdcc69fa0d4d035ebf756a3.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTPS 並非是應用層的一種新協議。 只是 HTTP 通信接口部分用SSL(Secure Socket Layer)和TLS(Transport Layer Security) 協議代替而已。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TLS的前身是SSL"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/06/06a28df9ebcb71779e1a1a13222aa9df.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,HTTP直接和TCP通信,當使用SSL時,則演變成先和SSL通信,再由SSL和TCP通信了,簡言之,所謂HTTPS其實就是身披SSL協議這層外殼的HTTP"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SSL協議使用通信雙方的客戶證書以及CA根證書,允許客戶/服務器應用以一種不能被偷聽的方式通信,在通信雙方間建立起了一條安全的、可信任的通信通道。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1.加密分爲對稱加密和非對稱加密(也叫公開密鑰加密)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)對稱加密的意思就是,加密數據用的密鑰,跟解密數據用的密鑰是一樣的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)對稱加密的優點在於加密、解密效率通常比較高。速度快,對稱性加密通常在消息發送方需要加密大量數據時使用,算法公開、計算量小、加密速度快、加密效率高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)缺點在於,數據發送方、數據接收方需要協商、共享同一把密鑰,並確保密鑰不泄露給其他人。此外,對於多個有數據交換需求的個體,兩兩之間需要分配並維護一把密鑰,這個帶來的成本基本是不可接受的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)非對稱加密的意思就是,加密數據用的密鑰(公鑰),跟解密數據用的密鑰(私鑰)是不一樣的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公鑰就是公開的密鑰,誰都可以查到;私鑰就是非公開的密鑰,一般是由網站的管理員持有。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公鑰與私鑰的聯繫:簡單的說就是,通過公鑰加密的數據,只能通過私鑰解開。通過私鑰加密的數據,只能通過公鑰解開。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2.公開密鑰存在問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"問題一:公鑰如何獲取;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"問題二:數據傳輸僅單向安全(因爲私鑰加密數據,公鑰也能解開,所以只是單向安全)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)對於公鑰如何獲取這個問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏涉及兩個重要概念:證書、CA(證書頒發機構)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書:可以暫時把它理解爲網站的身份證。這個身份證裏包含了很多信息,其中就包含了上面提到的公鑰。當訪問相應網站時,他就會把證書發給瀏覽器;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書來自於CA(證書頒發機構)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)證書可能存在的問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書是僞造的:壓根不是CA頒發的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書被篡改過:比如將XX網站的公鑰給替換了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字簽名、摘要是證書防僞非常關鍵的武器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“摘要”就是對傳輸的內容,通過hash算法計算出一段固定長度的串。然後,在通過CA的私鑰對這段摘要進行加密,加密後得到的結果就是“數字簽名”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明文 --> hash運算 --> 摘要 --> 私鑰加密 --> 數字簽名"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字簽名只有CA的公鑰才能夠解密。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書格式,需要關注的有幾個點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書包含了頒發證書的機構的名字 -- CA"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書內容本身的數字簽名(用CA私鑰加密)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書持有者的公鑰"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書籤名用到的hash算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中CA本身有自己的證書,稱爲根證書。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)對於完全僞造的證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況對證書進行檢查"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書頒發的機構是僞造的:瀏覽器不認識,直接認爲是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書頒發的機構是確實存在的,於是根據CA名,找到對應內置的CA根證書、CA的公鑰。用CA的公鑰,對僞造的證書的摘要進行解密,發現解不了。認爲是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)篡改過的證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"檢查證書,根據CA名,找到對應的CA根證書,以及CA的公鑰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用CA的公鑰,對證書的數字簽名進行解密,得到對應的證書摘要AA"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據證書籤名使用的hash算法,計算出當前證書的摘要BB"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對比AA跟BB,發現不一致--> 判定是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.3 HTTPS流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.客戶端發起HTTPS請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.服務端響應,下發證書(公開密鑰證書)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.客戶端檢查證書,如果證書沒問題,那麼就生成一個隨機值,然後用證書(公鑰)對該隨機值進行加密。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.將經過公鑰加密的隨機值發送到服務端(非對稱加密),以後客戶端和服務器的通信就可以通過這個隨機值進行加密解密了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5.服務端用私鑰解密後,得到了客戶端傳過來的隨機值,然後把內容通過該值進行對稱加密。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6.後期的數據傳輸都是基於該隨機值進行加密解密。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0a/0a58e9a53013009f430c66f5e810923b.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.4 對於公鑰獲取這個問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"涉及到證書和CA(證書頒發機構)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書可能存在的問題:1.證書是僞造的,壓根不是CA頒發的;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.證書被篡改過"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字簽名和摘要是證書防僞非常關鍵的武器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明文--->hash算法--->摘要---->私鑰加密----->數字簽名"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數字簽名只有CA的公鑰才能夠解密"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CA證書本身有自己的證書,稱爲根證書。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)對於完全僞造的證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況對證書進行檢查"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書頒發的機構是僞造的:瀏覽器不認識,直接認爲是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"證書頒發的機構是確實存在的,於是根據CA名,找到對應內置的CA根證書、CA的公鑰。用CA的公鑰,對僞造的證書的數字簽名進行解密,發現解不了。認爲是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)篡改過的證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"檢查證書,根據CA名,找到對應的CA根證書,以及CA的公鑰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用CA的公鑰,對證書的數字簽名進行解密,得到對應的證書摘要AA"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據證書籤名使用的hash算法,計算出當前證書的摘要BB"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對比AA跟BB,發現不一致--> 判定是危險證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.5 客戶端證書"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTPS中還可以使用客戶端證書,以客戶端證書進行客戶端認證,證明服務器正在通信的對方始終是預料之內的客戶端。想獲取證書時,用戶得自行安裝客戶端證書。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現狀是,安全性極高的認證機構可頒發客戶端證書但僅用於特殊用途的業務,比如網上銀行等。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15.6 HTTPS問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.與純文本相比,加密通信會消耗更多的CPU及內存資源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於HTTPS還需要做服務器、客戶端雙方加密及解密處理,因此會消耗CPU和內存等硬件資源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和HTTP通信相比,SSL通信部分消耗網絡資源,而SSL通信部分,由因爲要對通信進行處理,所以時間上又延長了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SSL慢分兩種,一種是指通信慢;另一種是指由於大量消耗CPU及內存等資源,導致處理速度變慢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.購買證書需要開銷。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十六、WebSocket"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/63/633c586a63b8556b252e311e639c75f5.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.1 WebSocket API"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.WS和WSS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket資源URL採用了自定義模式,ws表示純文本通信,wss表示使用加密信道通信(TCP+TLS)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket的主要目的,是在瀏覽器中的應用與服務器之間提供優化的、雙向通信機制。可是,WebSocket的連接協議也可以用於瀏覽器之外的場景,可以通過非HTTP協商機制交換數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.接收文本和二進制數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket通信只涉及消息,應用代碼無需擔心緩存、解析、重建接收到的數據。比如,服務器發來了一個1MB的淨荷,應用的onmessage回掉只會在客戶端接收到全部數據時纔會被調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"淨荷可以是文本也可以是二進制數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器接收到新消息後,如果是文本數據,會自動將其轉換成DOMString對象,如果是二進制數據或Blob對象(Blob對象一般代表一個不可變的文件對象或原始數據。如果你不需要修改它或者不需要把它切分成更小的塊,這種格式是理想的,若需要處理則選擇ArrayBuffer更合適),會直接將其轉交給應用。唯一可以多餘設置的,就是告訴瀏覽器把接收到的二進制數據轉換成ArrayBuffer而非Blob"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ba/babcbc7cd77a53ee8ca818b38b1ad2db.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"➊ 如果接收到二進制數據,將其強制轉換成 ArrayBuffer "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.發送文本和二進制數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端就可以隨時發送或接收 UTF-8 或二進制消息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的send()方法是異步的:提供的數據會在客戶端排隊,而函數則立即返回。特別是傳輸大文件的時候,千萬別因爲返回快,就認爲數據已經發送出去了。可以通過bufferedAmount屬性來監控瀏覽器中排隊的數據量。ws.bufferedAmount"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.子協議協商"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket協議對每條消息的格式事先不作任何假設:僅用一位標記消息是文本還是爲二進制,以便客戶端和服務器有效地解碼數據,而除此之外的消息內容就是未知的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,與 HTTP 或 XHR 請求不同——它們是通過每次請求和響應的 HTTP 首部來溝通元數據, WebSocket 並沒有等價的機制。因此,如果需要溝通關於消息的元數據,客戶端和服務器必須達成溝通這一數據的子協議。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket爲此提供了一個簡單便捷的子協議協商API。客戶端可以在初次連接握手時,告訴服務器自己支持哪種協議:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/361bbde90e60e41ac7495500f7f48aed.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket 構造函數可以接受一個可選的子協議名字的數組,通過這個數組,客戶端可以向服務器通告自己能夠理解或希望服務器接受的協議。這個協議數組會發送給服務器,服務器可以從中挑選一個。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果子協議協商成功,就會觸發客戶端的 onopen 回調,應用可以查詢 WebSocket 對"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"象上的 protocol 屬性,從而得知服務器選定的協議。另一方面,服務器如果不支持客戶端聲明的任何一個協議,則 WebSocket 握手是不完整的,此時會觸發 onerror 回調,連接斷開。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.2 WebSocket協議"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket通信協議包含兩個高層組件:開放性HTTP握手用於協商連接參數,二進制消息分幀機制用於支持低開銷的基於消息的文本和二進制數據傳輸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.二進制分幀層"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket使用了自定義的二進制分幀格式,把每個消息切分成一或多個幀,發送到目的地之後再組裝起來,等到接收到完整的消息後再通知接收端。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/62/6247ce983c6d32e124871f26b1fec88c.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幀:最小的通信單位,包含可變長度的幀首部和淨荷部分 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"消息:一系列幀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIN:表示當前幀是不是消息的最後一幀。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"操作碼(4位):表示被傳輸幀的類型:文本(1),二進制(2)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"掩碼位表示淨荷是否有掩碼(只適用於客戶端發送給服務器的消息)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"算下來,服務器發送的每個WebSocket幀會產生2~10字節的分幀開銷。而客戶端必鬚髮送掩碼鍵,這又會增加4字節,結果就是6~14字節的開銷。除此之外,沒有其他元素據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.WebSocket的多路複用及隊首阻塞"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket很容易發生隊首阻塞的情況:消息可能會被分成一個或多個幀,但不同消息的幀不能交錯發送,因爲沒有與HTTP2.0分幀機制中“流ID”對等的字段。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket不支持多路複用,還意味着每個WebSocket連接都需要一個專門的TCP連接。對於HTTP1.x而言,由於瀏覽器針對每個來源有連接數量限制,因此可能會導致問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然通過HTTP2.0傳輸WebSocket幀的官方規範尚未發佈,但相對來說容易很多。因爲HTTP2.0內置了流的多路複用,只要通過HTTP2.0的分幀機制來封裝WebSocket幀,多個WebSokcet連接就可以在一個會話中傳輸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.協議擴展"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)多路複用擴展"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個擴展可以將WebSocket的邏輯連接獨立出來,實現共享底層的TCP連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)壓縮擴展"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"給WebSocket協議增加了壓縮功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如前所述,每個 WebSocket 連接都需要一個專門的 TCP 連接,這樣效率很低。多路複用擴展解決了這個問題。它使用“信道 ID”擴展每個 WebSocket 幀,從而實現多個虛擬的 WebSocket 信道共享一個 TCP 連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類似地,基本的 WebSocket 規範沒有壓縮數據的機制或建議,每個幀中的淨荷就是應用提供的淨荷。雖然這對優化的二進制數據結構不是問題,但除非應用實現自己的壓縮和解壓縮邏輯,否則很多情況下都會造成傳輸載荷過大的問題。實際上,壓縮擴展就相當於 HTTP 的傳輸編碼協商。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要使用擴展,客戶端必須在第一次的Upgrade握手中通知服務器,服務器必須選擇並確認要在商定連接中使用的擴展。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.HTTP升級協商"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在交換數據之前,客戶端必須與服務器協商適當的參數以建立連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用HTTP完成握手有幾個好處。首先,讓WebSocket與現有HTTP基礎設施兼容:WebSocket服務器可以運行在80和443端口上,這通常是對客戶端唯一開放的端口。其次,讓我們可以重用並擴展HTTP的Upgrade流,爲其添加自定義的WebSocket"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首部,以完成協商。 "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be1d1092c30a778df325ee99df3344b6.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與瀏覽器中客戶端發起的任何連接一樣,WebSocket請求也必須遵守同源策略:瀏覽器會自動在升級握手請求中追加Origin首部,遠程服務器可能使用CORS判斷接受或拒絕跨源請求。要完成握手,服務器必須返回一個成功的“Switching Protocols”(切換協議)響應,並確認選擇了客戶端發送的哪個選項:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e8/e8d1005661322a025f12812c86015b48.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e7/e72e17e1bc4193cc2beb46bd861667e8.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果握手成功,該連接就可以用作雙向通信信道交換WebSocket消息。從此以後,客戶端與服務器之間不會再發生HTTP通信,一切由WebSocket協議接管。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.3 WebSocket使用場景及性能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.請求和響應流"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket是唯一一個能通過同一個TCP連接實現雙向通信的機制,客戶端和服務器隨時可以交換數據。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.消息開銷"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用消息會被拆分爲一或多個幀,每個幀會添加2~14字節的開銷。而且,由於分幀是按照自定義的二進制格式完成的, UTF-8 和二進制應用數據可以有效地通過相同的機制編碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)SSE會給每個消息添加5個字節,但僅限於UTF-8內容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)HTTP 1.x 請求(XHR 及其他常規請求)會攜帶 500~800 字節的 HTTP 元數據,加上 cookie"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)HTTP 2.0 壓縮 HTTP 元數據,這樣可以顯著減少開銷"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.數據效率及壓縮"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除非應用通過細緻優化自己的二進制淨荷實現自己的壓縮邏輯,同時也針對文本消息實現自己的壓縮邏輯,否則傳輸數據過程中一定會產生很大的字節開銷!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.自定義應用協議"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.4 圖解HTTP上解釋"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket技術主要是爲了解決Ajax和Comet裏XMLHttpRequest附帶的缺陷所引起的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket是建立在HTTP基礎上的協議,因此連接的發起方仍然是客戶端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Websocket只需要一次HTTP握手,所以說整個通訊過程是建立在一次連接/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的信息,直到你關閉請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)WebSocket的主要特點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)推送功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持由服務器向客戶端推送數據的推送功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)減少通信量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只要建立起WebSocket連接,就希望一直保持連接狀態。和HTTP相比,不但每次連接時的總開銷減少,而且由於WebSokcet的首部信息很小,通信量也相應減少了;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)握手請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了實現WebSocket通信,在HTTP連接建立之後,需要完成一次“握手”的步驟。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了實現WebSocket通信,需要用到HTTP的Upgrade首部字段,告知服務器通信協議發生改變,以達到握手的目的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Key:記錄握手需要的鍵值;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Protocol:記錄使用的子協議;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)握手響應"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於之前的請求,返回狀態碼101 Switching Protocols的響應。成功握手確立WebSocket連接之後,通信時不再使用HTTP的數據幀,而採用WebSockte獨立的數據幀;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Accept:該字段值是由握手請求中的Sec-WebSocket-Key的字段值生成的;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ea/ea51efa2351d25dc91b895a1b685fd77.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.5 WebSocket優點(對比參照HTTP協議)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://yq.aliyun.com/articles/633679?spm=a2c4e.11153940.0.0.5e293dd6CNm3ZH","title":""},"content":[{"type":"text","text":"https://yq.aliyun.com/articles/633679?spm=a2c4e.11153940.0.0.5e293dd6CNm3ZH"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)支持雙向通信,實時性更強;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)更好的二進制支持;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)較少的控制開銷:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連接創建後,ws客戶端、服務端進行數據交換時,協議控制的數據包頭部較小。在不包含頭部的情況下,服務端到客戶端的包頭只有2~10字節(取決於數據包長度),客戶端到服務端的的話,需要加上額外的4字節的掩碼。而HTTP協議每次通信都需要攜帶完整的頭部;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)支持擴展:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ws協議定義了擴展,用戶可以擴展協議,或者實現自定義的子協議(比如支持自定義壓縮算法等)。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.6 如何建立連接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket複用了HTTP的握手通道。具體指的是,客戶端通過HTTP請求與WebSocket服務端協商升級協議。協議升級完成後,後續的數據交換則遵照WebSocket的協議。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.客戶端:申請協議升級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,客戶端發起協議升級請求。可以看到,採用的是標準的HTTP報文格式,且只支持GET方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Connection: Upgrade:表示要升級協議"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Upgrade: websocket:表示要升級到websocket協議。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Version: 13:表示websocket的版本。如果服務端不支持該版本,需要返回一個Sec-WebSocket-Versionheader,裏面包含服務端支持的版本號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Key:與後面服務端響應首部的Sec-WebSocket-Accept是配套的,提供基本的防護,比如惡意的連接,或者無意的連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.服務端:響應協議升級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回狀態碼101表示協議切換"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.7 數據幀格式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端、服務器數據的交換,離不開數據幀格式的定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket客戶端、服務端通信的最小單位是幀,由一個或多個幀組成一條完整的消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送端:將消息切割成多個幀,併發送給服務端;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收端:接收消息幀,並將關聯的幀重新組裝成完整的消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幀內容見上面二小結"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.8 數據傳遞"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一旦WebSocket客戶端、服務端建立連接後,後續的操作都是基於數據幀的傳遞。WebSocket根據opcode來區分操作的類型。比如0x8表示斷開連接,0x0-0x2表示數據交互。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.數據分片"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket的每條消息可能被切分成多個數據幀。當WebSocket的接收方收到一個數據幀時,會根據FIN的值來判斷,是否已經收到消息的最後一個數據幀。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIN=1表示當前數據幀爲消息的最後一個數據幀,此時接收方已經收到完整的消息,可以對消息進行處理。FIN=0,則接收方還需要繼續監聽接收其餘的數據幀。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"opcode在數據交換的場景下,表示的是數據的類型。0x01表示文本,0x02表示二進制。而0x00比較特殊,表示延續幀(continuation frame),顧名思義,就是完整消息對應的數據幀還沒接收完。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.9 連接保持+心跳"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket爲了保持客戶端、服務端的實時雙向通信,需要確保客戶端、服務端之間的TCP通道保持連接沒有斷開。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有些場景,客戶端、服務端雖然長時間沒有數據往來,但仍需要保持連接,這個時候可以採用心跳來實現:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送方->接收方:ping"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收方->發送方:pong"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ping、pong的操作,對應的是WebSocket的兩個控制幀,opcode分別是0x9、0xA"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如:ws.ping('', false, true);"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.10 Sec-WebSocket-Key/Accept的作用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sec-WebSocket-Key/Sec-WebSocket-Accept在主要作用在於提供基礎的防護,減少惡意連接、意外連接。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16.11 數據掩碼的作用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebSocket協議中,數據掩碼的作用是增強協議的安全性。但數據掩碼並不是爲了保護數據本身(因爲算法本身是公開的,運算也不復雜),而是爲了防止早期版本的協議中存在的代理緩存污染攻擊等問題。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十七、狀態碼301和302的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)什麼是301重定向?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"301重定向/跳轉一般,表示本網頁永久性轉移到另一個地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"301是永久性轉移(Permanently Moved),SEO(搜索引擎優化)常用的招式,會把舊頁面的PR(永久居留)等信息轉移到新頁面;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)什麼是302重定向?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"302重定向表示臨時性轉移(Temporarily Moved),當一個網頁URL需要短期變化時使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)301重定向與302重定向的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"301重定向是永久的重定向,搜索引擎在抓取新內容的同時也將舊的網址替換爲重定向之後的網址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"302重定向是臨時的重定向,搜索引擎會抓取新的內容而保留舊的網址。因爲服務器返回302代碼,搜索引擎認爲新的網址只是暫時的。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十八、強緩存引起問題的解決方案(如何更新網站資源)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存在問題:發佈時資源更新問題,更新了資源,但是用戶每次請求時依然從緩存中獲取原來的資源,除非用戶清除或者強刷新,否則看不到最新的效果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、利用304定向重定向,讓瀏覽器本地緩存即協商緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:協商緩存還是需要和瀏覽器通信一次。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、強制瀏覽器使用本地緩存,不和服務器通信。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:無法更新緩存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、通過更新頁面中引用的資源路徑,讓瀏覽器主動放棄緩存,加載新資源。例如在文件名稱後面增加一個版本號,下次上線時更改版本號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺點:當有多個靜態緩存文件,只有一個文件更改時,卻需要更新所有文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"採用數據摘要算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"想要解決這個問題:考慮到讓url的修改與文件內容相關聯即只有文件內容變化時,纔會導致相應的url的變更,從而實現文件級別的精確緩存控制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了進一步提升網站性能,會把靜態資源和動態網頁分集羣部署,靜態資源會被部署到CDN節點上,網頁中引用的資源也會變成對應的部署路徑。當我要更新靜態資源的時候,同時也會更新html中的引用。若這次發佈,同時改了頁面結構和樣式,也更新了靜態資源對應的url地址,現在要發佈代碼上線,咱們是先上線頁面,還是先上線靜態資源?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先部署頁面,再部署資源:在二者部署的時間間隔內,如果有用戶訪問頁面,就會在新的頁面結構中加載舊的資源,並且把這個舊版本的資源當做新版本緩存起來,其結果就是:用戶訪問到了一個樣式錯亂的頁面,除非手動刷新,否則在資源緩存過期之前,頁面會一直執行錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先部署資源,再部署頁面:在部署時間間隔之內,有舊版本資源本地緩存的用戶訪問網站,由於請求的頁面是舊版本的,資源引用沒有改變,瀏覽器將直接使用本地緩存,這種情況下頁面展現正常;但沒有本地緩存或者緩存過期的用戶訪問網站,就會出現舊版本頁面加載新版本資源的情況,導致頁面執行錯誤,但當頁面完成部署,這部分用戶再次訪問頁面又會恢復正常了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述先部署誰都有問題,這是因爲採用的是覆蓋式發佈,用 待發布資源 覆蓋 已發佈資源,就有這種問題。解決它也好辦,就是實現 非覆蓋式發佈。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大公司的靜態資源優化方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.配置超長時間的本地緩存 —— 節省帶寬,提高性能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.採用內容摘要作爲緩存更新依據 —— 精確的緩存控制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.靜態資源CDN部署 —— 優化網絡請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.更新資源發佈路徑實現非覆蓋式發佈 —— 平滑升級"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"十九、滑動窗口原理"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19.1 TCP滑動窗口(發送窗口和接收窗口)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP滑動窗口主要有兩個作用,一是提供TCP的可靠性,二是提供TCP的流控特性(TCP的滑動窗口是動態的)。同時滑動窗口機制還體現了TCP面向字節流的設計思路。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP的Window是一個16bit位字段,它代表的是窗口的字節容量,也就是TCP的標準窗口最大爲2^16 - 1 = 65535個字節。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.對於TCP會話的發送方,任何時候在其發送緩存內的數據都可以分爲4類,“已經發送並得到對端ACK的”,“已經發送但還未收到對端ACK的”,“未發送但對端允許發送的”,“未發送且對端不允許發送”。“已經發送但還未收到對端ACK的”和“未發送但對端允許發送的”這兩部分數據稱之爲發送窗口(中間兩部分)。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/66/66950375a0681e12f77cddb2cd50972f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當收到接收方新的ACK對於發送窗口中後續字節的確認是,窗口滑動,滑動原理如下圖。 "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3a/3a778f474406a133d5253d037c5e7610.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當收到ACK=36時窗口滑動"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.對於TCP的接收方,在某一時刻在它的接收緩存內存有3種。“已接收”,“未接收準備接收”,“未接收並未準備接收。其中“未接收準備接收”稱之爲接收窗口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.發送窗口和接收窗口關係"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP是雙工的協議,會話的雙方都可以同時接收、發送數據。TCP會話的雙方都各自維護一個”發送窗口“和一個”接收窗口“。其中各自的”接收窗口“大小取決於應用、系統、硬件的限制(TCP傳輸速率不能大於應用的數據處理速率)。各種的”發送窗口“則要求取決於對端通告的”接收窗口“,要求相同。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.滑動窗口實現面向流的可靠性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最基本的傳輸可靠性來源於”確認重傳“機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP的滑動窗口的可靠性也是建立在”確認重傳“基礎上的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送窗口只有收到對端對於本段發送窗口內字節的ACK確認,纔會移動發送窗口的左邊界。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收窗口只有在前面所有的段都確認的情況下才會移動左邊界。當在前面還有字節未接收但收到後面字節的情況下,窗口不會移動,並不對後續字節確認。以此確保對端會對這些數據重傳。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19.2 TCP協議上的網絡協議分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前建立在TCP協議上的網絡協議特別多,有telnet,ssh,有ftp,有http等等。這些協議又可以根據數據吞吐量來大致分成兩大類:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)交互數據類型,例如telnet,ssh,這種類型的協議在大多數情況下只是做小流量的數據交換,比如說按一下鍵盤,回顯一些文字等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)數據成塊類型,例如ftp,這種類型的協議要求TCP能儘量的運載數據,把數據的吞吐量做到最大,並儘可能的提高效率。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19.3 滑動窗口協議相關術語"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"滑動窗口本質上是描述接受方的TCP數據報緩衝區大小的數據,發送方根據這個數據來計算自己最多能發送多長的數據。如果發送發收到接收方的窗口大小爲0的TCP數據報,那麼發送方將停止發送數據,等到接收方發送窗口大小不爲0的數據報的到來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於滑動窗口協議介紹了三個術語,分別是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"窗口合攏:當窗口從左邊向右邊靠近的時候,這種現象發生在數據被髮送和確認的時候。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"窗口張開:當窗口的右邊沿向右邊移動的時候,這種現象發生在接受端處理了數據以後。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"窗口收縮:當窗口的右邊沿向左邊移動的時候,這種現象不常發生"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19.4 舉一個例子來說明一下滑動窗口的原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP並不是每一個報文段都會回覆ACK的,可能會對兩個報文段發送一個ACK,也可能會對多個報文段發送1個ACK【累計ACK】,比如說發送方有1/2/3 3個報文段,先發送了2,3 兩個報文段,但是接收方期望收到1報文段,這個時候2,3報文段就只能放在緩存中等待報文1的空洞被填上,如果報文1,一直不來,報文2/3也將被丟棄,如果報文1來了,那麼會發送一個ACK對這3個報文進行一次確認。
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉一個例子來說明一下滑動窗口的原理:
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1\\. 假設32~45 這些數據,是上層Application發送給TCP的,TCP將其分成四個Segment來發往internet
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2\\. seg1 32~34 seg3 35~36 seg3 37~41 seg4 42~45 這四個片段,依次發送出去,此時假設接收端之接收到了seg1 seg2 seg4
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3\\. 此時接收端的行爲是回覆一個ACK包說明已經接收到了32~36的數據,並將seg4進行緩存(保證順序,產生一個保存seg3 的hole)
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4\\. 發送端收到ACK之後,就會將32~36的數據包從發送並沒有確認切到發送已經確認,提出窗口,這個時候窗口向右移動
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5\\. 假設接收端通告的Window Size仍然不變,此時窗口右移,產生一些新的空位,這些是接收端允許發送的範疇
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6\\. 對於丟失的seg3,如果超過一定時間,TCP就會重新傳送(重傳機制),重傳成功會seg3 seg4一塊被確認,不成功,seg4也將被丟棄
"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就是不斷重複着上述的過程,隨着窗口不斷滑動,將真個數據流發送到接收端,實際上接收端的Window Size通告也是會變化的,接收端根據這個值來確定何時及發送多少數據,從對數據流進行流控。原理圖如下圖所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f1/f1ca6a70069bdf4c94b047abe0e91ac0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19.5 滑動窗口動態調整"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要是根據接收端的接收情況,動態去調整Window Size,然後來控制發送端的數據流量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端不斷快速發送數據,服務器接收相對較慢,看下實驗的結果"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"a. 包175,發送ACK攜帶WIN = 384,告知客戶端,現在只能接收384個字節"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"b. 包176,客戶端果真只發送了384個字節,Wireshark也比較智能,也宣告TCP Window Full"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"c. 包177,服務器回覆一個ACK,並通告窗口爲0,說明接收方已經收到所有數據,並保存到緩衝區,但是這個時候應用程序並沒有接收這些數據,導致緩衝區沒有更多的空間,故通告窗口爲0, 這也就是所謂的零窗口,零窗口期間,發送方停止發送數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"d. 客戶端察覺到窗口爲0,則不再發送數據給接收方"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"e. 包178,接收方發送一個窗口通告,告知發送方已經有接收數據的能力了,可以發送數據包了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"f. 包179,收到窗口通告之後,就發送緩衝區內的數據了."}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/ca7539647bde6ec6cf26efa937a6cb44.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結一點,就是接收端可以根據自己的狀況通告窗口大小,從而控制發送端的接收,進行流量控制 "}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二十、Chrome中的from memory cache與from disk cache"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1\\. 在瀏覽器開發者工具的Network的Size列會出現的三種情況"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"from memory cache"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"from disk cache"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"資源本身大小(比如:13.6K)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2\\. 三級緩存原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先查找內存,如果內存中存在,從內存中加載;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果內存中未查找到,選擇硬盤獲取,如果硬盤中有,從硬盤中加載;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果硬盤中未查找到,那就進行網絡請求;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加載到的資源緩存到硬盤和內存;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3\\. HTTP狀態碼及區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"200 form memory cache"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不訪問服務器,一般已經加載過該資源且緩存在了內存當中,直接從內存中讀取緩存。瀏覽器關閉後,數據將不存在(資源被釋放掉了),再次打開相同的頁面時,不會出現from memory cache。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"200 from disk cache"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不訪問服務器,已經在之前的某個時間加載過該資源,直接從硬盤中讀取緩存,關閉瀏覽器後,數據依然存在,此資源不會隨着該頁面的關閉而釋放掉下次打開仍然會是from disk cache。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"200 大小(如3.4k)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問服務器,size顯示爲實際資源的大小"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"304 Not Modified"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"訪問服務器,發現數據沒有更新,服務器返回此狀態碼。然後從緩存中讀取數據。這種在請求頭中有兩個請求參數:If-Modified-Since 和 If-None-Match。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| 狀態碼 | 類型 |說明 |"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| :----: | :----: | :----: |"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| 200 | form memory cache | 不請求網絡資源,資源在內存當中 |"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| 200 | form disk ceche | 不請求網絡資源,在磁盤當中 |"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| 200 |資源大小數值|從服務器下載最新資源|"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|304|報文大小|請求服務端發現資源沒有更新,使用本地資源 |"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上是chrome在請求資源是最常見的兩種http狀態碼,由此可見樣式表一般在磁盤中,不會緩存到內存中去,因爲css樣式加載一次即可渲染出網頁。但是腳本卻可能隨時會執行,如果腳本在磁盤當中,在執行該腳本需要從磁盤中取到內存當中來,這樣的IO開銷是比較大的,有可能會導致瀏覽器失去響應。"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎大家關注公衆號(回覆“nginx”獲取本節的思維導圖,回覆“書籍”獲取大量前端學習資料,回覆“前端視頻”獲取大量前端教學視頻)"}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9a/9a3d2734b53d10fd2fd73b6e994d989d.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章