@[TOC]html
原文連接:blog.csdn.net/jieqiang3/a…java
這篇主要梳理一下web相關的原理知識。因爲我的水平有限,文章中可能會出現錯誤,若是你以爲哪一部分有錯誤,或者發現了錯別字等內容,歡迎在評論區告訴我。web
TCP(傳輸控制協議),負責在不可靠的傳輸信道之上提供可靠的抽象層。 TCP的存在價值主要專一於可靠兩個字,也就是它可以確保發送的全部字節流都可以完整的被接收到,並且到達客戶端的順序也是同樣。但凡事都有兩面性,若是精確性獲得了保證的話,就會犧牲必定的速度和效率。 先來看TCP的鏈接和斷開鏈接,也就是咱們都熟悉的三次握手四次揮手:算法
通過這個三次握手之後客戶端和服務端就能夠互相傳輸數據了。這裏客戶端能夠在發送ACK分組以後當即發送數據,服務端必須在接收到第三部的ACK分組之後才能傳輸數據。api
那麼接下來又有一個問題了,爲何是三次握手呢?爲何不是兩次,或者四次五次握手呢? TCP是一個雙向通訊協議,爲了數據傳輸的可靠性,通訊雙方會維護一個序列號,以標識發出去的數據包裏有哪些數據是被對方收到的。若是隻有兩次握手的話,服務端就沒法確認他的序列號是否獲得了確認,只有客戶端的序列號獲得了確認。而四次,五次握手的話則會形成資源的浪費。能夠確定的是,三次握手是在確保可靠性的基礎上,性能上的最優解。跨域
那麼爲何要四次揮手呢??? TCP的一端發出FIN報文段以後,僅僅表示這一端沒有數據要發送給另外一端了,可是他仍然能接受來自另外一端的數據。因此如上面所示客戶端先發送消息給服務端說我不發數據消息給你了,而後服務端發給客戶端確認,而後服務端再發消息給客戶端跟客戶端說我也不發數據消息給你了,而後客戶端回覆確認,而後服務端關閉,以後客戶端等待了之後也關閉。因此這就是須要四次揮手才能來關閉鏈接的緣由。瀏覽器
在實際的網絡傳輸過程當中,很容易出現擁塞的現象,TCP對於擁塞控制主要有如下四個算法:緩存
要理解這四個算法的話,首先先要理解擁塞窗口(cwnd)的概念:發送端對從客戶端接受確認(ACK)以前能夠發送數據的限制。 TCP的擁塞控制主要依賴於這個擁塞窗口來控制,首先三次握手的時候,客戶端和服務端會經過ACK分組告知對方本身的接收窗口(rwnd)的大小,而後三次握手ACK消息成功傳遞之後雙方就能夠互相發消息了,可是若是在一開始鏈接剛創建就向網絡中發送大量的數據包的話,就很容易致使網絡中路由器的緩存空間耗盡,發聲擁塞,因此即客戶端與服務器之間最大能夠傳輸(未經 ACK 確認的)數據量取 rwnd 和 cwnd 變量中的最小值。而這個cwnd的值會根據算法慢慢指數級增長,同時也設置了一個ssthresh門限值,若是cwnd達到這個值之後會讓cwnd增加變得平滑,也就是慢啓動。安全
擁塞預防的原理就是加入ssthresh門限值來限制 cwnd的增加,當cwnd超過該值之後,慢啓動過程結束,進入擁塞避免階段。擁塞避免的主要思想是加法增大,也就是cwnd的值再也不指數級往上升,開始加法增長。此時當窗口中全部的報文段都被確認時,cwnd的大小加1,cwnd的值就隨着RTT開始線性增長,這樣就能夠避免增加過快致使網絡擁塞,慢慢的增長調整到網絡的最佳值。性能優化
TCP的每個報文段都有一個定時器,叫作重傳定時器,若是重傳定時器超時而且沒有獲得數據確認的話,TCP就會對該報文段進行重傳處理,這裏TCP也就把網絡定義爲擁塞的狀態,會進行以下操做:
快速恢復是當收到3個重複ACK的時候纔會觸發,進入快速恢復階段:
上面介紹TCP的時候咱們已經看出來,TCP是基於鏈接的協議,創建了可靠的鏈接之後雙方纔能互相傳遞數據。而UDP正好跟TCP相反,UDP是面向非鏈接的協議,不與對方創建鏈接,直接把數據包發過去就行了的一種協議,UDP是盡最大努力交付的。 因此UDP的傳輸速度快,由於不須要什麼三次握手這種操做,並且UDP 沒有擁塞控制。UDP只適合用於一些只須要傳遞少許數據,對可靠性要求不是很高的應用場景,好比一些多媒體通訊的要求。
首先這裏咱們先來弄清楚一個概念,也就是TLS和SSL的區別,這也是我在看《web性能權威指南》時候一臉懵逼的。 TLS:傳輸層安全協議,用於兩個應用程序之間提供保密性和數據完整性。該協議由兩層組成:TLS記錄協議和TLS握手協議。 SSL:安全套接字層,位於可靠的面向鏈接的網絡層協議和應用層協議之間的一種協議層。SSL經過互相認證、使用數字簽名確保完整性、使用加密確保私密性,以確保客戶端和服務端之間的安全通信。由兩層組成:SSL記錄協議和SSL握手協議。 二者有什麼聯繫呢? TLS是以SSL 3.0爲基礎於1999年做爲SSL的新版本推出的,也就是說SSL是TLS的前世,提供更強大的支持。 如今應該弄清楚二者之間的區別之後,咱們就能夠來看TLS協議的原理了。
先來看流程圖:
注意這裏的隨機對稱密鑰是對稱加密,因此運算速度很是快,而服務器公鑰只用於加密"對話密鑰"自己,這樣就減小了加密運算的消耗時間。另外爲了優化性能,爲了彌補完整的TLS握手所帶來的額外的延遲和計算量,TLS提供了會話恢復功能,即在多個鏈接間共享這個協商後的安全密鑰,也就是上面第9部中生成的用公鑰加密的隨機對稱密鑰: 在內部服務器會給每一個客戶端保存一個會話ID和協商後的會話參數就是密鑰,對應客戶端也會有保存,這樣下次鏈接的時候就能夠告訴對方本身有保存上次鏈接的資料,這樣就能夠迅速鏈接,節省一次往返,至關於加了一層緩衝層。
TLS的握手過程基本確保了傳輸的信息加密過;有身份認證機制,A想發消息給B,A能確保發送到的是B而不是中間被人攔截接受;有校驗機制,能保證信息不會被篡改,這三個機制就是TLS出現的意義所在。
HTTP 1.1出現的一個重要目標就是爲了提高HTTP 1.0的性能。 接下來列舉幾個比較重要的性能優化點:
咱們知道一個TCP鏈接建立的時候要通過三次握手才能進行數據傳遞,並且還可能存在慢啓動延遲,因此若是每次去調一個請求若是都須要建立一個TCP鏈接的話那麼顯然是很不友好的。因此HTTP 1.1.的時候加入了持久化鏈接,也就是支持場鏈接和請求的流水線處理,在一個TCP鏈接上能夠傳送多個HTTP請求和響應,HTTP1.1中是默認開啓keep-alive的。這個持久化鏈接不會被一直保留,在空閒一段時間後被關閉,節約資源。 可是若是僅僅經過一個持久化鏈接,而後不斷在這個鏈接上發送一個又一個的請求的話,通過實踐效率並不理想,因此HTTP 1.1又加入了向同一個IP地址重複創建多個TCP鏈接的機制。你們能夠用wireshark抓數據包試試,你會發如今幾秒鐘裏面會創建好幾個目的端口爲80的TCP鏈接。如今大多數現代瀏覽器,包括桌面和移動瀏覽器,都支持每一個主機打開 6 個鏈接。
HTTP管道化在個人理解其實就是,客戶端發送多個http以FIFO隊列的形式請求到服務端之後,服務端返回這些請求的響應按照一樣的FIFO隊列的形式輸出到客戶端。這裏,服務端會嚴格按照FIFO隊列輸出,若是服務端併發處理且優先級較高的請求處理時間比較長的話,那麼優先級較低且先處理完的請求會被放入服務器的緩衝區,等待優先級較高的請求先回復之後再回復較低的,以FIFO隊列形式。 在出現管道化以前,http請求都是順序請求的,也就是說下一個請求只有在當前請求的響應被徹底接受之後才能發送。因此這就有一個問題,http兩個請求之間會有很大的延遲。 通常來講,只有冪等的請求才會進行管道化操做,冪等的請求其實就是屢次操做都不會改變結果的請求,好比GET和HEAD。因此通常PUT和POST都不會被管道化,
2012年Google提出了SPDY方案,優化了HTTP 1.X的請求延遲、安全性等問題,而HTTP 2.0能夠說是在SPDY的基礎上設計出來的升級版plus。 HTTP 2.0最大的特色就是在不應動HTTP 語義,HTTP方法、狀態碼、URI及首部字段等等核心的東西的基礎上,突破了HTTP 1.X的性能限制,最大的改變就是HTTP 2.0新加了一個二進制分幀層。
從上面的圖中咱們能夠看到,二進制分幀層的位置是在應用層和傳輸層之間,把HTTP 1.1中的首部信息封裝到Headers幀中,把request body封裝到了DATA 幀中。HTTP 2.0會把全部的傳輸信息分割爲更小的消息和幀,並對他們採用二進制格式的編碼形式。 先來看幾個在HTTP 2.0中的新概念:HTTP 2.0簡而言之,就是在一個TCP鏈接上,創建任意數量的雙向字節流,在每條字節流上以消息的形式來傳遞消息,而消息的是由一個或者多個幀組成的。 這裏這個幀是能夠亂序傳遞的,每一個幀都有一個幀首部,幀首部上會有一個流標識符,最後會根據這個流標識符來從新組裝順序。 另外爲了進一步增長效率,HTTP 2.0有一個首部壓縮的機制,HTTP 2.0在客戶端和服務端都會用一個「首部表」來跟蹤和存儲以前發送的鍵值對,對於相同的數據,就不會再經過每次請求和響應來發送了。 若是首部發生變化了,那麼只須要發送變化了的數據放到Headers幀裏,新增或者修改的首部幀就會跟蹤到「首部表」,而後不斷的更新變化。
WebSocket 是HTML5中一種全新的web通訊技術,是一種最通用最靈活的一個傳輸機制,真正實現了瀏覽器與服務器的全雙工實時通訊。 WebSocket 資源 URL 採用了自定義模式:ws 表示純文本通訊(如 ws://example. com/socket),wss 表示使用加密信道通訊(TCP+TLS)。WebSocket協議能夠算是HTTP協議的一種補充,一種加強,以HTTP協議爲基礎,跟HTTP協議最大的區別在我理解就是:HTTP協議中只有你發送了一個request,你纔會獲得一個response,是輪詢機制的,也就是說response是被動的,不能主動發起。而WebSocket中服務器則隨時能夠向客戶端傳遞消息,全雙工實時通訊。
這是Android中引用了org.java-websocket:Java-WebSocket:1.3.9後使用WebSocket一些api回調,使用起來簡單明瞭很方便。
try {
webSocketClient = new WebSocketClient(new URI(webSocketUrl)) {
@Override
public void onOpen(ServerHandshake handshakedata) {
}
@Override
public void onMessage(final String message) {
}
@Override
public void onClose(int code, String reason, boolean remote) {
}
@Override
public void onError(Exception ex) {
}
@Override
public void onWebsocketPong(WebSocket conn, Framedata f) {
super.onWebsocketPong(conn, f);
}
};
複製代碼
上面講區別的時候講到WebSocket是基於HTTP協議的,其實說的直白點,WebSocket是借用了HTTP的協議來完成了一部分握手,起到向下兼容現有瀏覽器的做用。 WebSocket協議有兩部分組成:握手和數據傳輸。
咱們來看一個RFC6455文檔中給出的一個客戶端握手消息實例:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
複製代碼
這裏咱們能夠看到WebSocket使用HTTP來創建鏈接,但其中又定義了一系列心的header域:Upgrade: websocket Connection: Upgrade來告訴服務器,我發起的是WebSocket協議,並經過Sec-WebSocket-Key、Sec-WebSocket-Protocol、Sec-WebSocket-Version三個值來校驗以及告訴服務器Draft協議版本。 這以後服務器會返回一個標準的HTTP的Response消息來通知客戶端我已經接受請求切換爲WebSocket協議了:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
複製代碼
創建了 WebSocket 鏈接後,客戶端就能夠隨時發送或接收 UTF-8 或二進制消息。 WebSocket 提供的是一條雙向通訊的信道,也就是說,在同一個 TCP 鏈接上,能夠雙向傳輸數據,再也不須要Request消息。WebSocket 通訊只涉及消息,應用代碼無需擔憂緩衝、解析、重建接收到的數據。 另外,WebSocket的數據幀是有序的。
websocket爲了保持客戶端、服務端的實時雙向通訊,須要確保客戶端、服務端之間的TCP通道保持鏈接沒有斷開,因此會有一個心跳包機制:
如下內容摘自《web性能權威指南》,我以爲頗有參考價值:
不管什麼網絡,也無論所用網絡協議是什麼版本,全部應用都應該致力於消除或減 少沒必要要的網絡延遲,將須要傳輸的數據壓縮至最少。這兩條標準是經典的性能優 化最佳實踐,是其餘數十條性能準則的出發點。
- 減小DNS查找 每一次主機名解析都須要一次網絡往返,從而增長請求的延遲時間,同時還會阻 塞後續請求。
- 重用TCP鏈接 儘量使用持久鏈接,以消除 TCP 握手和慢啓動延遲
- 減小HTTP重定向 HTTP 重定向極費時間,特別是不一樣域名之間的重定向,更加費時;這裏面既有 額外的 DNS 查詢、TCP 握手,還有其餘延遲。最佳的重定向次數爲零。
- 使用CDN(內容分發網絡) 把數據放到離用戶地理位置更近的地方,能夠顯著減小每次 TCP 鏈接的網絡延 遲,增大吞吐量。這一條既適用於靜態內容,也適用於動態內容
- 去掉沒必要要的資源 任何請求都不如沒有請求快。
- 在客戶端緩存資源 應該緩存應用資源,從而避免每次請求都發送相同的內容。
- 傳輸壓縮過的內容 傳輸前應該壓縮應用資源,把要傳輸的字節減至最少:確保對每種要傳輸的資源 採用最好的壓縮手段。
- 消除沒必要要的請求開銷 減小請求的 HTTP 首部數據(好比 HTTP cookie),節省的時間至關於幾回往返 的延遲時間。
- 並行處理請求和響應 請求和響應的排隊都會致使延遲,不管是客戶端仍是服務器端。這一點常常被忽 視,但卻會無謂地致使很長延遲。
- 針對協議版本採起優化措施 HTTP 1.x 支持有限的並行機制,要求打包資源、跨域分散資源,等等。相對而 言,HTTP 2.0 只要創建一個鏈接就能實現最優性能,同時無需針對 HTTP 1.x 的 那些優化方法。
參考資料: