從 1989 年 HTTP 0.9 發布開始,互聯網的發展已經近三十年,各種網絡概念也是層出不窮。如 Https, WebSocket, Http 2.0, spdy 等等。
相信大家或多或少能從各種途徑,獲取到詳細的資料。 這里我想通過另一個維度,來看看它們是如何演化而來,為什么這么設計,解決了什么業務場景,以及一些基本的入門實踐等。
我們試圖從以下幾個問題出發,解釋這篇文章的初衷。
1. 當我們打開瀏覽器訪問一個網頁時,它是如何將數據安全的發送給我們的?
2. Http 2.0 為何時隔10年才出現,它解決了什么問題?
3. Socket 和 Http 什么關系?
4. Web socket 又是什么?它是工作在哪一層的?
5. 想要更輕量,更有效率的數據傳輸如何做?
為了解釋以上問題,我們先從網絡基礎知識開始,這對解釋以上問題很重要。
1. 網絡協議
1.1 七層協議
OSI model: The Open Systems Interconnection model
一種概念模型,由國際標準化組織(ISO)提出,一個試圖使各種計算機在世界范圍內互連為網絡的標準框架。
通俗的說,為了全世界的計算機之間相互通信,而設計的一種協議框架,所有需要聯網的設備或軟件都需要遵循它的規則。另外,也為了盡可能避免所有功能混雜在一起,讓不同公司或組織更聚焦在一個領域上,才有了現在的分層模型。
我們可以看到在每一層,根據實際應用場景,都有對應的協議被設計出來。
從上面的模型我們可以知道,大的結構上,分為面向上層應用與面向網絡媒介兩個部分。應用層上主要是面向數據與數據的傳輸,網絡媒介主要是面向網絡通信與硬件。
1.2 TCP/IP
基于 OSI 協議模型衍生而來,現今使用最多的就是 TCP/IP 協議棧了。注意這里的 TCP/IP 協議棧并不只是指 TCP 與 IP 協議的合集,它是一種更簡化的 OSI 模型。
通過下圖可以看到它與 OSI 模型區別:
從 tcp/ip 協議規范來看,應用層,表示層與傳輸層被合并在一起。主要原因是這三層,在當今的實際使用中有相當多耦合關系,也很難將它們刻意的分離。
實際上現在的 TCP/IP 模型更加簡單明了,更適應現在的應用場景。
除了以上的協議棧外,還有當今兩個重要的協議:TLS 與 SPDY 。
1.3 TLS/SSL
TLS/SSL 是當今一個比較重要的安全傳輸協議,它不在上面所列的協議棧中。但它被設計位于應用層與傳輸層之間。以確保數據在傳輸之前是安全的,但又不破壞原有的數據協議。應用層協議(例如:HTTP、FTP、Telnet等等)能透明的創建于TLS 協議之上。
我們經常聽到的 HTTPS, 這個 S 指的就是 secure (安全),同時分為 SSL 和 TLS 協議。它被 Netscape 公司在 1994 年推出。
SSL: (Secure Socket Layer,1994 年推出, 第一版作者: 塔希爾·蓋莫爾)。
SSL通過互相認證、使用數字簽名確保完整性、使用加密確保私密性,以實現客戶端和服務器之間的安全通訊。
TLS: (Transport Layer Security, 1999 年推出)
它建立在SSL 3.0協議規范之上,作用同于 SSL, 與 SSL 3.0 差別極小,可以理解為SSL 3.1。
無論是 SSL 還是 TLS 都分為兩層:? 握手協議與記錄協議
* SSL 因為被報有各種安全問題,所以基本上現在所使用的都是 TLS。
以下是建立連接的過程:
它必須建立在可靠的數據傳輸基礎上,所以一般是在TCP之上。
同時,如果一但使用了 SSL/TLS 做安全驗證,建立連接的時間實際上是會變長的,如圖:
1.4 SPDY
SPDY(發音如英語:speedy),2010 年 9 月 一種開放的網絡傳輸協議,由 Google 開發,用來發送網頁內容的協議誕生,用以最小化網絡延遲,提升網絡速度,優化用戶的網絡使用體驗。它基于 TCP 的應用層協議,也是 HTTP/2 的前身。它最早被應用在 Chrome 6 。
設計 SPDY 的目的在于降低網頁加載時間。通過優先級和多路復用,SPDY使得只需要創建一個TCP連接即可傳送網頁內容及圖片等資源。SPDY 中廣泛應用了 TLS 加密,傳輸內容也均以 gzip 或 DEFLATE 格式壓縮(與 HTTP 不同,HTTP的頭部并不會被壓縮)。另外,除了像 HTTP 的網頁服務器被動的等待瀏覽器發起請求外,SPDY 的網頁服務器還可以主動推送內容。
SPDY 并不用于取代 HTTP,它只是修改了 HTTP 的請求與應答在網絡上傳輸的方式;這意味著只需增加一個 SPDY 傳輸層,現有的所有服務端應用均不用做任何修改。 當使用 SPDY 的方式傳輸,HTTP 請求會被處理、標記簡化和壓縮。比如,每一個 SPDY 端點會持續跟蹤每一個在之前的請求中已經發送的 HTTP 報文頭部,從而避免重復發送還未改變的頭部。而還未發送的報文的數據部分將在被壓縮后被發送。
Google 之所以改動 HTTP 協議而不是 TCP/IP,是因為改 HTTP 只需更新 Browser 和 web server 就行了,而改動 TCP/IP 牽扯面太廣,需要更新所有的路由器,服務器和客戶端的操作系統。
1.3 TCP
無論應用層的協議如何定義、使用,都要基于一條已經連接的通路去發送與接收數據,我們可以試著從我們最熟悉的數據傳輸層開始,來了解連接的建立與傳輸。
這一層有兩個重要的協議,TCP 與 UDP。
TCP 協議的運行可分為三個階段:連接創建(connection establishment)、數據傳送(data transfer)和連接終止(connection termination)
連接創建
TCP 用三次握手(three-way handshake)過程創建一個連接。在連接創建過程中,很多參數要被初始化,例如序號被初始化以保證按序傳輸和連接的強壯性。
一對終端同時初始化一個它們之間的連接是可能的。但通常是由一端打開一個套接字(socket)然后監聽來自另一方的連接,這就是通常所指的被動打開(passive open)。服務器端被被動打開以后,用戶端就能開始創建主動打開(active open)。
1. 客戶端通過向服務器端發送一個 SYN 來創建一個主動打開,作為三路握手的一部分。客戶端把這段連接的序號設定為隨機數A。
2. 服務器端應當為一個合法的 SYN 回送一個 SYN/ACK。ACK 的確認碼應為 A+1,SYN/ACK 包本身又有一個隨機產生的序號B。
3. 最后,客戶端再發送一個 ACK。當服務端受到這個 ACK 的時候,就完成了三路握手,并進入了連接創建狀態。此時包的序號被設定為收到的確認號 A+1,而響應號則為 B+1。
連接重試
如果服務器端接到了客戶端發的SYN后回了SYN-ACK后客戶端掉線了,服務器端沒有收到客戶端回來的ACK,那么,這個連接處于一個中間狀態,即沒成功,也沒失敗。于是,服務器端如果在一定時間內沒有收到的TCP會重發SYN-ACK。在Linux下,默認重試次數為5次,重試的間隔時間從1s開始每次都翻倍,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s,第5次發出后還要等32s才知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才會斷開這個連接。
數據傳輸
主機收到一個 TCP 包時,用兩端的IP地址與端口號來標識這個 TCP 包屬于哪個session。
TCP 或 UDP 使用了端口號(Port number)的概念來標識發送方和接收方的應用層,通常也稱為 Internet sockets。對每個 TCP 連接的一端都有一個相關的16位的無符號端口號分配給它們。端口被分為三類:眾所周知的、注冊的和動態/私有的。眾所周知的端口號是由因特網賦號管理局(IANA)來分配的,并且通常被用于系統一級或根進程。眾所周知的應用程序作為服務器程序來運行,并被動地偵聽經常使用這些端口的連接。例如:FTP (20 and 21), SSH (22), TELNET (23), SMTP (25), HTTP over SSL/TLS (443), and HTTP (80)等。注冊的端口號通常被用來作為終端用戶連接服務器時短暫地使用的源端口號,但它們也可以用來標識已被第三方注冊了的、被命名的服務。動態/私有的端口號在任何特定的TCP連接外不具有任何意義。可能的、被正式承認的端口號有65535個。
超時重傳
另外,對于 TCP 數據傳輸 ,有一個重要的概念提到的就是 超時重傳。這個機制是進一步來證明為何 TCP 是一種可靠的傳輸。
發送方使用一個保守估計的時間作為收到數據包的確認的超時上限。如果超過這個上限仍未收到確認包,發送方將重傳這個數據包。每當發送方收到確認包后,會重置這個重傳定時器。
這個超時定義就是 RTT (來回通訊延遲(Round-trip delay time)) 在的定義。
連接終止
使用了四次握手 (four-way handshake) ,終止一個 TCP 連接
更詳細的資料可以參考 Wikipedia
1.4 UDP
除了可靠的 TCP 連接,還有一種稱為不可靠的連接機制: 用戶數據包協議(User Datagram Protocol,縮寫為UDP),是一個簡單的面向數據報的傳輸層協議。此協議由 David P. Reed 在 1980 年設計出來,并納入 RFC 768 成為正式的規范。
兩者各有利弊,UDP 無需建立連接,不保證傳輸的可靠性,也沒有重試機制,所以它的效率更高,當然數據傳輸的質量也不保證。
在TCP/IP模型中,UDP為網絡層以上和應用層以下提供了一個簡單的接口。UDP只提供數據的不可靠傳遞,它一旦把應用程序發給網絡層的數據發送出去,就不保留數據備份(所以UDP有時候也被認為是不可靠的數據報協議)。UDP在IP數據報的頭部僅僅加入了復用和數據校驗(字段)。
網絡上的眾多使用UDP協議的關鍵應用一定程度上是相似的。這些應用包括域名系統(DNS)、簡單網絡管理協議(SNMP)、動態主機配置協議(DHCP)、路由信息協議(RIP)和某些影音流服務等等。
1.5 Socket
上面我們提到了 Socket ?按字面的意思是插座的意思,也就是建立一個通道,進行通信或連接。
根據 OSI 模型,它是基于傳輸層 (TCP/UDP) 上的一種具體實現。有了 TCP/UDP 協議,雙方可以按照協議的規定,搭建起連接,一但連接搭建起來,我們就稱它為 Socket 連接已建立,相當于插到插座上了。
在計算機科學中
它通常代表網絡套接字(Network socket),又譯網絡插槽,是電腦網絡中進程間互相通信的一種機制。使用互聯網協議(Internet Protocol)為通信基礎的網絡套接字,稱為 Internet socket。因為互聯網協議的流行,現代絕大多數的網絡套接字,都是屬于 Internet socket。
在操作系統中
它是一種提供進程間通信的機制。通常會為應用程序提供一組應用程序接口(API),稱為套接字接口(socket API)。應用程序可以通過套接字接口,來使用網絡套接字,以進行數據交換。最早的套接字接口來自于4.2 BSD,因此現代常見的套接字接口大多源自Berkeley套接字(Berkeley sockets)標準。在套接字接口中,以IP地址及通信端口組成套接字地址(socket address)。遠程的套接字地址,以及本地的套接字地址完成連接后,再加上使用的協議(protocol),這個五元組(five-element tuple),作為套接字對(socket pairs),之后就可以彼此交換數據。
例如,再同一臺計算機上,TCP 協議與 UDP 協議可以同時使用相同的port而互不干擾。 操作系統根據套接字地址,可以決定應該將數據送達特定的進程或線程。這就像是電話系統中,以電話號碼加上分機號碼,來決定通話對象一般。
所以也可以理解為 socket = IP + Port
這里有 C 寫的 Socket Demo 大家可以參考。其中 accept 函數也就是實現了三次了握手的過程。
2. 應用層
2.1 HTTP
應用層里最常用的協議之一,Hypertext Transfer Protocol (HTTP) 超文本傳輸協議。
HTTP 歷史
- 1965 年,一個叫 Ted Nelson 的 26 歲年輕人,提出了超文本與超媒體的概念。(真是英雄出少年)
- 1989 年,時隔多年,一個叫 Tim Berners-Lee 和他的團隊在 CERN(歐洲核子研究組織)基于超文本概念,開始了 WorldWideWeb 項目。也就是我們現在耳熟能詳的 WWW (萬維網),將超文本系統與互聯網結合在一起,將超文本數據發送出去。第一個版本只有一個方法,叫 GET ,它可以從服務器請求一個頁面到客戶端,這個頁面就是 HTML 頁。
- 1991 年,第一個 HTTP 的文檔 HTTP 0.9 定義出來。世界上第一個網站在 CERN 搭建。
- 1996 年,HTTP 1.0 標準發布。Dave Raggett 領導的 HTTP Working Group (HTTP WG) 擴充了 HTTP 的協議,增加了富媒體信息,安全協議,header 域等。
- 1997 年,HTTP 1.1 標準被制定。
- 2015 年,HTTP 2.0 發布
關于 HTTP Session
一個 HTTP session 是一個網絡的 request-response 序列。一個 HTTP 客戶端初始時請求建立一個 TCP 連接到服務器的指定端口(通常為 80)。一個 HTTP 服務端一直監聽來自客戶端的 request 信息。基于接收的 request, 服務器返回狀態行,如 "HTTP/1.1 200 OK"。
2.1 HTTP 0.9 ~ 2.0 進化之路
HTTP 0.9 :
只有 GET 一只方法,不支持請求頭
HTTP 1.0 :
增加了請求頭,除了 GET 外,多了很多方法,如 POST,? HEAD, PUT 等。
HTTP 1.1 :
由于 HTTP 1.0 只保持短暫的連接,每次請求后,TCP 連接都會被關閉,導致大量的時間耗費在了建立連接上。另一個問題是, HTTP 1.0 的請求像一個隊列,遵循 FIFO (先進先出)的原則,當第一個請求的 Response 返回后,第二個 Request 才能發出,這就是所說的 head of line blocking (HOL blocking)。
所以 HTTP 1.1 對這兩個主要問題進行了改進
a. 支持了持久連接,在一個TCP連接上可以傳送多個HTTP請求和響應,減少了建立和關閉連接的消耗和延遲。
b.允許客戶端不用等待上一次請求結果返回,就可以發出下一次請求,但服務器端必須按照接收到客戶端請求的先后順序依次回送響應結果,以保證客戶端能夠區分出每次請求的響應內容,這樣也顯著地減少了整個下載過程所需要的時間。
此外,1.1 協議中還支持了身份認證、狀態管理和Cache緩存等機制,可以很方便的實現如斷點續傳等場景。
HTTP 1.0 與 HTTP 1.1 的主要區別可以看這里:Key Differences between HTTP/1.0 and HTTP/1.1
HTTP Pipelining
它是將多個HTTP 請求整批提交的技術,而在發送過程中不需先等待服務端的回應。
請求結果管線化使得 HTML 網頁加載時間動態提升,特別是在具有高延遲的連接環境下。在寬帶連接中,加速不是那么顯著的,因為需要服務器端應用 HTTP/1.1 協議:服務器端必須按照客戶端的請求順序恢復請求,這樣整個連接還是先進先出的,對頭阻塞(HOL blocking)可能會發生,造成延遲。
HTTP 2.0:
實際上 HTTP 1.1 使用了很長時間后,一直沒有經過大的改進,基本上也就是在原來的基礎上進行小范圍的協議優化與擴充。直到 Google 推出了 SPDY 后,HTTP 2.0 基于 SPDY 的基礎上,被正式提出。
它主要解決了 HTTP 1.0 或 1.1 中始終存在的 HOL blocking 問題。
它并沒有破壞原有的 HTTP 結構,僅僅是將數據采用了二進制傳輸,比起以前的文本傳輸,更緊湊與高效。在二進制分幀層上,HTTP2.0 會將所有傳輸的信息分割為更小的消息和幀,并對它們采用二進制格式的編碼,其中 HTTP1.x 的首部信息會被封裝到 Headers 幀,而我們的 request body 則封裝到 Data 幀里面。
新的二進制分幀機制改變了客戶端與服務器之間交換數據的方式。 為了說明這個過程,我們需要了解 HTTP/2 的三個概念:
數據流:已建立的連接內的雙向字節流,可以承載一條或多條消息。
消息:與邏輯請求或響應消息對應的完整的一系列幀。
幀:HTTP/2 通信的最小單位,每個幀都包含幀頭,至少也會標識出當前幀所屬的數據流。
這些概念的關系總結如下:
1. 所有通信都在一個 TCP 連接上完成,此連接可以承載任意數量的雙向數據流。
2. 每個數據流都有一個唯一的標識符和可選的優先級信息,用于承載雙向消息。
3. 每條消息都是一條邏輯 HTTP 消息(例如請求或響應),包含一個或多個幀。
4. 幀是最小的通信單位,承載著特定類型的數據,例如 HTTP 標頭、消息負載,等等。 來自不同數據流的幀可以交錯發送,然后再根據每個幀頭的數據流標識符重新組裝。
這里的關鍵是數據流有了優先級,而不必像以前按順序處理,服務端可以根據自定義的優先級處理 request。
還有一個大的改進是服務器推送,HTTP 2.0 不必像以前一樣,必須依賴 request 才發送 response ,打破了嚴格的請求-響應語義。即使客戶端沒有 Request 請求,服務端也可以發送 Response 給客戶端。
* tips: 大家可以在命令行下,輸入以下命令,看看兩者有何不同。
telnet www.google.com 80
GET / HTTP/1.0
GET / HTTP/1.1
2.2. HTTPS
關于 HTTPS 中的 S ,即安全傳輸,之前已經講到了,它是在介于傳輸層與應用層之間的一層安全協議,基于可靠傳輸(如 TCP )之后,對信息的一層加密封裝。
相信大家可以在很多地方搜到 HTTPS 的詳細解釋。這里就不逐一展開了。我們只要記住以下幾點即可。
首先是,對稱加密與非對稱加密,兩種加密方式最大的區別在于,對稱加密沒有數據長度限制,非對稱有長度限制,所以要使用非對稱加密傳輸對稱加密的密鑰。
其次,非對稱加密中的公鑰,不能直接發送給對方,否則會產生中間人攻擊的場景。必須有一個可靠的第三方做中間人。這就是我們大家所熟知的 CA (數字證書的頒發機構)。
這個 CA 通常會根據域名來做服務器端的認證,而且 CA 的公鑰是被內置在瀏覽器或操作系統中的,所以 CA 的公鑰是不用傳輸的。這樣就避免了中間人的證書偽造。
之前有人已經寫過一篇 非常通俗易懂的文章 ,大家可以做參考。
2.3. WebSocket
提到應用層協議,其實還有一個比較流行的協議就是 WebSocket ,它在 2011 年被被IETF 定為標準 RFC 6455。
WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
為什么叫 WebSocket 呢?它和 Socket 什么關系?
首先 WebSocket 和 Socket 沒有半毛錢關系,它之所以這么叫,完全是為了利于理解。之所以強調 Web,是因為它可以基于 HTTP 連接進行協議切換。還記得之前 HTTP /1.1 里的 request header 嗎?我們可以定義 Upgrade: websocket Connection: Upgrade。然后服務器的 response 會返回:? HTTP/1.1 101 Switching Protocols。即可切換到 WebSocket 協議。
web socket 使用 ws 或 wss 做為統一的標識符。如:ws://www.abc.com
實踐:基于 node JS 的 WebSocket 客戶端與服務端開源項目
3. 總結
回答開始所提出的問題
1. 當我們打開瀏覽器訪問一個網頁時,它是如何將數據安全的發送給我們的?
如果有域名的先通過 DNS 解析域名,如果是 IP 直接訪問的就不用了。然后排開底層網絡協議不看,從傳輸層開始,先進行三次握手,有 S 的進行加密通道的建立,拿到傳輸密鑰。然后基于應用層的協議,比如 HTTP ,進行 Request 與 Response 的數據傳輸。根據使用的 HTTP 協議不同,建立的連接有可能是持續的,也有可能是一次性的。
2. Http 2.0 為何時隔10年才出現,它解決了什么問題?
HTTP 1.1 出來后,基本上在上面做補充與小調整,沒有實質的變化。直到十年后,憑借 Google 強大的技術研發能力,加上現代的網絡基礎設施越來越好,提出了二進制流傳輸方式,解決了 HOL blocking 問題。并最終成為了 HTTP /2.0 的標準。目前,在應用層上,可優化的空間越來越小,接下來的瓶頸更多是傳輸層了,但傳輸的層的變化,會牽扯所有現有的瀏覽器與操作系統,所以這一塊的演化沒有想像的那么快。也許將來有一天,會有更新的技術出現。
3. Socket 和 Http 什么關系?
Socket 是通道,在網絡領域,它是一個 Network Socket ,建立兩個端之間的一條管道,進行數據的傳輸。HTTP 是基于這個管理,進行超文本的傳輸。簡單的講, Socket 一但建立,你可以傳輸任何東西,HTTP 也好, FTP 也好。取決于你的應用協議是什么。
4. Web socket 又是什么?它是工作在哪一層的?
Web Socket 只是一個雙工通信概念,它為了解決 HTTP 只能通過 Request 與 Response 的形式發送信息的模式,無論是客戶端還是服務端都可以主動的發送數據。HTTP /2.0 也加入了此概念。但 WebSocket 發明的比它早,所以它是前輩。
它工作在傳輸之上,應用層的一種,可以理解為 HTTP 的升級版。因為它可以利用 HTTP 的協議,進行升級使用,所以稱為 Web Socket.
5. 想要更輕量,更有效率的傳輸如何做?
你可以直接面向 Socket 編程,傳輸層之上進行開發,自己發明更輕量,更高效的應用層協議。現有的操作系統,對 TCP 接口有非常標準,完善的編程接口,開發也不是難事了。所以你可根據應用的場景,定制更合適的應用層協議。
推薦買一本 Socket 網絡編程開始學。