一、Chrome Developer Network Tab
Cheome Developer作為現在前端開發者最常用的開發調試工具,其具有前端可以涉及到的各方面的強大功能,為我們的開發和定位問題提供了極大地便利。其中Network Tab是相當常用的一個功能板塊。通過它的XHR、JS、CSS、Img等子Tab我們可以捕獲到所有基於應用層的HTTP/HTTPS協議的網絡請求,可以查看到該次請求和響應的所有頭信息和內容。
Network Tab
展示了針對每一個HTTP請求的所有屬性,包括:
其中Connection ID為傳輸層TCP協議的連接ID。關於這點會在下一個章節提到。
Headers主要展示了此次請求的狀態,還有請求和響應的頭部信息,頭部信息是HTTP交互雙方進行作業的依據:
Headers中的大多數Key對於有經驗開發者來說並不陌生,不需要在這里介紹了。但還是需要提到兩個key:
content-type作為描述交互內容數據MIME格式的key意義相當重大,我們在實際開發中發出請求缺接收到不到任何東西,如果請求其他部分沒問題的話,很可能就是因為前后端的content-type不匹配的原因導致的。
referer作為描述請求發起者所屬域的key,也是非常有用的。
1.通過它我們可以對網站進行訪問量統計;
2.可以對任何資源的訪問做域的限制(防盜鏈),比如說:我引用一個QQ空間的圖片URL放到我自己HTTP服務器serve的網頁的<img />上,當我訪問該頁面的時候並沒有拿到這個圖片,取而代之的是一個訪問受限制的站位圖片。也就是說QQ空間的服務器在接收到資源請求的時候,是對referer做了檢測的,如果非QQ空間的頁面發起的請求是無法正常獲取到目標圖片的。referer本身是個錯誤的單詞,正確寫法應該為referrer,譯為介紹人,描述了是在哪個域下進行請求資源或者跳轉到某個URL的操作。后來為了向下兼容HTTP協議,這個錯誤的單詞一直沒有被修改。
需要注意的是:當我們直接從瀏覽器地址欄訪問某資源時,此時referer為空,因為此時並不存在有真正的介紹人,這是一個憑空產生的請求,並不是從其他任何地方鏈過去的。
Response展示了服務端響應的內容,Preview是根據Headers中的雙方的Content-Type的MIME類型加工后的方便開發者瀏覽的帶格式的數據內容:
Cookie展示了在此次請求中瀏覽器Headers中所帶Cookie,以及HTTP服務器端對瀏覽器端Cookie的設置:
Timing 整個請求從准備發出到結束的生命周期時序:
對於有經驗的開發者來,從Headers、Preview與Response、Cookie中能獲取到相當有用的信息。對於Timing Tab,它更接近底層,展示了瀏覽器端發起一個HTTP請求的全過程,按照Chrome官方解釋,Timing中各階段描述如下:
1. Queuing(排隊中)
如果一個請求排隊,則表明:
1)請求被渲染引擎推遲,因為它被認為比關鍵資源(如腳本/樣式)的優先級低。這經常發生在 images(圖像) 上。
2)這個請求被擱置,在等待一個即將被釋放的不可用的TCP socket。
3)這個請求被擱置,因為瀏覽器限制。在HTTP 1協議中,每個源上只能有6個TCP連接,這個問題將在下一面的章節中提到。
4)正在生成磁盤緩存條目(通常非常快)。
2.Stalled/Blocking (停止/阻塞)
發送請求之前等待的時間。它可能因為進入隊列的任何原因而被阻塞。這個時間包括代理協商的時間。
3.Proxy Negotiation (代理協商)
與代理服務器連接協商花費的時間
4.DNS Lookup (DNS查找)
執行DNS查找所用的時間。 頁面上的每個新域都需要完整的往返(roundtrip)才能進行DNS查找。當本地DNS緩存沒有的時候,這個時間可能是有一段長度的,但是比如你一旦在host中設置了DNS,或者第二次訪問,由於瀏覽器的DNS緩存還在,這個時間就為0了。
5.Initial Connection / Connecting (初始連接/連接)
建立連接所需的時間, 包括TCP握手/重試和協商SSL。
6.SSL
完成SSL握手所用的時間,如果是HTTPS的話
7.Request Sent / Sending (請求已發送/正在發送)
發出網絡請求所花費的時間。 通常是幾分之一毫秒。
8.Waiting (TTFB) (等待)
等待初始響應所花費的時間,也稱為`Time To First Byte`(接收到第一個字節所花費的時間)。這個時間除了等待服務器傳遞響應所花費的時間之外,還捕獲到服務器發送數據的延遲時間。這些情況可能會導致高TTFB:1.客戶端和服務器之間的網絡條件差;2.服務器端程序響應很慢。
9.Content Download / Downloading (內容下載/下載)
接收響應數據所花費的時間。從接收到第一個字節開始,到下載完最后一個字節結束。
通過對請求發出和響應的每個階段的理解,我們就能分析出當前HTTP請求存在的問題,並據此解決問題。
二、客戶端與服務端通過HTTP協議的交互過程
在HTTP協議RFC2616的描述中,HTTP作為應用層協議,推薦並默認使用TCP/IP作為傳輸層協議,且其他任何可靠的傳輸層協議也都可以被HTTP協議采用和使用。也就是說假如UDP是"可靠"的,HTTP也可以走在UDP上面。目前市面上流行的瀏覽器的HTTP請求普遍遵守這個原則並采用TCP/IP作為傳輸層協議。
下面是捕獲的一個對通過XMLHttpRequest對https://localhost:3000/api/syncsystemstatus發起的HTTPS GET請求:
在上個章節中有提到Connection ID是TCP連接的ID, 表明了此次資源的請求是通過哪一個TCP連接完成的。
通常情況下我們使用Fiddler、Charles或者Chome Developer工具只能對HTTP/HTTPS請求抓包,這里我們使用WireShark對更底層的協議連接進行封包抓取,並分析上面所提到的這個連接從建立到結束的整個過程。WireShark抓包截圖如下:
說明:由於筆者使用Webpack的dev-server給localhost:3000做了正向代理,並開啟了HTTPS,由於服務器並未開啟HTTPS,所以dev-server到服務器並不是HTTPS而是HTTP1.1,192.168.11.94就是dev-server的IP,可以將其看作localhost:3000,也就是客戶端瀏覽器。192.168.100.101為dev-server正向代理到的目的地,也是請求要發送到的HTTP服務器。簡單來講該例子就是從瀏覽器(192.168.11.14)通過XMLHttpRequest對象發起了一個到服務器(192.168.100.101)的HTTP1.1請求。
客戶端和服務器交互過程如下:
No.x號為WireShark封包列表中最左側的列,記錄每個封包在該次抓取中的編號,並依次遞增。
No.1:瀏覽器(192.168.11.94)向服務器(192.168.100.101)發出連接請求,並發送SYN包,進入SYN_SEND狀態,等待服務器確認。這是TCP三次握手的第一次。
No.2:服務器(192.168.100.101)響應了瀏覽器(192.168.11.94)的請求,確認瀏覽器的SYN(ACK=J+1),並且自己也發送SYN包也就是SYN+ACK包,要求瀏覽器進行確認,此時了服務器進入SYN_RECV狀態。這是TCP三次握手的第二次。
No.3:瀏覽器(192.168.11.94)響應了服務器(192.168.100.101)的SYN+ACK包,向服務器發送確認包ACK(ACK=K+1),此包發送完畢,瀏覽器和服務器進入ESTABLISHED狀態,這是TCP三次握手的第三次,握手完成,TCP連接成功建立。
No.4:瀏覽器(192.168.11.94)發出一個HTTP請求到服務器(192.168.100.101)。
No.5:服務器(192.168.100.101)收到瀏覽器(192.168.11.94)發出的請求,並確認,然后開始發送數據。
No.6:服務器(192.168.100.101)發送狀態響應碼200到瀏覽器(192.168.11.94),表示數據傳輸成功並且完畢,content-type表明響應的內容文本需要被解析為JSON格式, OK結束。此時我們開發者通過判斷XHR的readyState為4以及status為200就可以得到服務器完整的返回數據並應用在前端邏輯或頁面展示上了。
對應第一章節中提到的Chrome Developer Network的請求時序圖:
1.發起第一個請求並完成連接的建立:No.1至No.4 對應時序圖中的第5步至第7步。XHR的readyState為0-2,初始化請求、發送請求並建立連接,
2.基於TCP連接的建立,通過HTTP協議進行數據傳輸:No.5對應時序圖中的第8步至第9步,XHR的readyState為3,正在交互中,開始數據。數據傳輸完畢后,readyState為4,status為200。
對於Fetch對象發起的請求也是如此的,只不過Fetch基於Promise封裝,readyState和status可以理解為是內部控制的,來決定resolve和reject的情況。筆者的項目其實是使用Fetch的,只是這里用XMLHttpRequest對象也就是Ajax來說明,容易理解一些。
針對No.1至No.3的TCP的三次握手示意圖:
SYN:Synchronize Sequence Numbers 同步序列編號。
SYN_SEND:請求連接,當你要訪問其它的計算機的服務時首先要發個同步信號給該端口,此時狀態為SYN_SENT,如果連接成功了就變為ESTABLISHED。
ACK:Acknowledgement 確認字符。在數據通信中,接收站發給發送站的一種傳輸類控制字符。表示發來的數據已確認接收無誤。在TCP/IP協議中,如果接收方成功的接收到數據,那么會回復一個ACK數據。通常ACK信號有自己固定的格式,長度大小,由接收方回復給發送方。
No.4才是是HTTP的包,這表明HTTP連接是基於TCP連接建立的。
其他標識符比如FIN、PSH、RST等可以參考這里,
而斷開連接時需要4次揮手,多1次是因為在主動要求斷開連接的那一方不知道被動斷開連接的那一方是否還有數據沒有傳輸完畢,被動斷開連接的那一方需要把回復已接收斷開消息和數據已經發送完畢分到2步內進行,無論真實情況下,在接收到主動方要斷開連接的消息時,還有沒有數據需要發送,這2步都必須分開。主動方會一直等待被動方發送FIN碼。其實也可以用3步完成,但是數據完整性就得不到保證了。有興趣的同學可以自行了解下,這里不做詳細解釋了。
三、HTTP因前序請求阻塞而導致后續請求沒法發起的問題
筆者目前開發的這個項目早期底層和服務端沒有做緩存優化的時候,從底層Go的接口返回數據給Node.js層,Node.js層再返回給前端界面。在底層接口沒優化的時候,一些操作是現場調用腳本,如果腳本執行耗時長,或者因為網絡抖動原因導致底層分布式集群各節點之間通信及慢,接口響應速度從幾十毫秒、幾百毫秒到幾秒甚至更長時間不等。前端是基於React.j的SPA應用,每個界面為了數據的准確性,在進入界面后會立即請求數據,並且后台還根據了當前路由維持了一個每15s更新數據的CronJob,定時刷新這個界面的數據。如果暴力的切換路由改變界面可以在短時間內創建大量的HTTP請求。在HTTP1.1下的性能表現極為糟糕,阻塞情況嚴重。在Chrome等瀏覽器中,針對同一個域下的HTTP1.1請求同時創建6條TCP連接,每條連接結束以后才能釋放出來給對另外一個資源的請求來使用。雖然和HTTP1.0相比,在性能上已有較大提升,但是並沒有本質的改變。以本項目為例,如果當瞬間發起滿10個請求后,只有前6個請求能夠分配6個不同的HTTP連接進行處理,后續4個請求只有等待這6個請求有任何一個釋放HTTP連接資源以后,才能繼續。也就是說前6個請求中如果最少耗時都在1s,那么后4個請求的最少Pending時間都在1s。而且接口的請求都是同一個域,走同一個API網關,無法通過像類似於請求CDN資源一樣,來把資源請求分散到不同的域下。在筆者暴力的操作下,這簡直是噩夢:
以getsnapshot這個接口為例,在不阻塞的情況下,其大致需要84ms來完成請求:
然而在發生阻塞后:
在串行響應的加持下,額...好恐怖。
開啟了webpack-dev-server的HTTPS(通過spdy模塊啟服務)后,瀏覽器默認啟用HTTP/2(HTTP2.0)協議:
(關於Chrome開發者工具Network Tab中的Connection ID列的解釋,可以參考這里)
依舊是暴力操作,瀏覽器在短時間內發起大量的請求。可以看到在ID為2693483的這個TCP連接上,在這個鏈接的管道中並發處理了的所有的HTTP資源請求:它們的開始都不依賴上一個請求的結束,而且可以並行響應,即后面的請求響應不會等待前面請求響應完成。
再回頭來看看HTTP1.X的keep-alive帶來的優化:在一定時間內,一個域下只要第一次建立HTTP連接成功,也就是3次握手成功,那么后面的HTTP不再新建立TCP連接管道,均使用該次連接建立成功以后的管道。避免了不必要的連接建立的握手過程以及斷開連接的揮手過程的耗時。實現了HTTP的持久連接和管道流水線(pipelining)。再加上瀏覽器對於HTTP1.1協議處理,都會針對同一個域開辟6個TCP連接,一定程度上緩解了瀏覽器端的資源加載壓力。
HTTP1.X雖然解決了HTTP0.X的一些問題,但它在效率上還存在有一些問題:
1. 串行的響應:即便瀏覽器能夠同時在一個連接的HTTP流水線管道里發起多個請求,服務器也能夠在這個管道里響應多個請求,但是請求在服務器端依舊是按照順序給出響應的,也就是說瀏覽器端接收數據的時候,必須是按照發起時的順序來接收。而且瀏覽器對管道流水線的的支持並不是太好,要么不支持,要么默認關閉的,需要手動設置開啟。對於請求性能和帶寬的利用率提高並未帶來實質性變化。對於這一點瀏覽器提供的單域名6個TCP/HTTP連接的優化還可以一定程度上緩解壓力,但是短時間內針對同一域名的請求發起了太多,響應也較慢,阻塞還是注定會發生的。
2. 請求-響應的數量太多的限制:大多數瀏覽器HTTP1.x對同一個域一個時間段內的請求-響應數量是有限制的,一般為6個,這導致瀏覽器對網絡帶寬和服務器資源的利用無法最大化。
既然服務器不能並行響應,那么僅僅在瀏覽器上能夠並行發起請求還有什么意義呢?最多也就只能先去排上隊而已。也就是說HTTP1.X即便有了keep-alive的加持,它不能算作是全雙工的協議,只能算半雙工的協議。
3.對客戶端和服務端性能消耗大:瀏覽器在HTTP1.X上提供的單域名6個TCP/HTTP連接優化,為了維持這6條連接,會導致在請求兩方的機器上都會有額外的性能開銷。
請看以下通過HTTP1.1協議發情請求的截圖:
瞬間並行發起5個請求,瀏覽器在HTTP1.1協議下做了最大優化:即很對同一的請求同時開啟多個(6個)TCP連接,並在它們上面創建了對應數量的可復用並且是持久化的HTTP連接來處理並發的多個HTTP請求,避免因串行響應而造成的隊頭阻塞。
當我們間隔一定時間去發起請求,每次都復用的是同一個TCP連接,另外五個出於閑置狀態:
這已經是在HTTP1.1協議下,不修改業務邏輯的條件下能達到的最大優化,還想要性能提高,恐怕就必須使用到一些合並資源請求、CDN資源分發等方法了。
而HTTP2.0協議改變了上述問題1的串行方式,允許多個請求並行和並發。允許在一個HTTP連接內發起多個的請求-響應,並且是多流並行的,卻又不依賴建立多個TCP連接。數據通過TCP層進行傳輸的時候,引入了二進制數據幀、流的概念,拋棄了HTTP1.X的基於文本格式的數據傳輸方式,因為這種方式必須按照順序進行請求-響應。轉而使用二進制幀來對數據進行歸類,在幀的頭部注入流的標識符,這樣瀏覽器收到數據之后,通過標識符再將不同流的數據合並在一起,可以並行錯亂或者分級優先地發送(比如遇到圖片和JS都一起請求的時候,可以給JS資源請求一個較高的有限值,使它被優先處理)。在服務端通過流的標識符進行重新歸類和組裝。極大地提高了發送效率,實現了並行且非阻塞的多路復用。說直白一點:無論客戶端還是服務器端,都可以一邊並行發送數據一邊並行接收數據。這也就是解決了上面提到的問題1.
對於數據幀內部來說,HTTP1.X的請求-響應首部被放在了HEADERS幀里面,內容被放到了DATA幀里面。
對於問題2解決:HTTP2.0對同一域名下所有請求都是基於流的,就是說在同一域名下,不管在客戶端上存在有多少資源的訪問,從理論上講也可以只建立一個HTTP連接的(實際上就是這樣的),通過流來區分不同的請求的數據,所以這一個連接就能完成整個頁面的資源加載和后期的數據請求,而不用擔心並發請求-響應的時候會不會出現數據錯亂,筆者認為這是相對於HTTP1.X的本質改變。服務器的開銷得以減少,處理能力得到大幅提升。
既然只有一個連接的,那么對資源的開銷問題也會得到大大的緩解,也就解決了問題3。
從上述的分析中可以得知:HTTP2.0並不是使傳輸層TCP變成了並行的連接,TCP傳輸層本身的因串行傳輸而帶來的阻塞是沒解決的,TCP依舊是一個獨木橋,僅僅是在應用層HTTP協議上進行了優化,但也正是這些重要的優化使HTTP2.0成為了全雙工的協議,單連接多資源的方式克服了TCP慢啟動帶來的負面影響,更加有效地利用了TCP連接,使連接性能得到了極大的提升。也充分地利用了TCP協議的帶寬來降低HTTP延遲,並且減少了連接的內存占用,單個連接的吞吐量增大,網略阻塞和丟包的恢復速度增快等。PS. 想要深入的理解HTTP2.0協議的讀者,可以自行搜索一些權威資料,這里不再做深入介紹了。
對通過傳統方式進行資源請求優化的影響:一旦HTTP2.0啟用后,我們可能會根據它的特點去改變一些我們之前對於靜態資源的處理,可以減少之前的前端方面在資源請求上的優化工作,特別是資源合並的以減少請求的手段,比如:壓縮到一個js文件以減少HTTP請求、精靈圖片、CSS合並等,前幾年在移動端也曾經流行過通過LocalStorage緩存靜態資源,以及Combo服務來合並HTTP請求做性能優化的方案,但在現在4G移動網絡以及HTTP2.0的普及下,也已經沒有太大的意義了。
關於HTTP1.1的persisdent-connection(HTTP1.0 keep-alive的替代)功能的介紹,傳送門。
這里再推薦一本書,有興趣的同學可以閱讀其中的11至14章節,傳送門。
四、SPDY的出現
上個段落我們提到了HTTP2.0協議針對HTTP1.X版本的優化,但是HTTP2.0在2015年年中才定稿,在這之前要實現HTTP2.0的一些特性通常使用由Google進行推廣的SPDY協議,該協議經歷了四個草案,大量的基於HTTP1.X和SSL/TLS上的優化被IETF采用,作為HTTP2.0的重要功能點,可以說SPDY協議是HTTP2.0出現的關鍵前奏。但其終歸是基於HTTP1.X的擴展,除了有類似HTTP2.0的性能提升外,也會有一些HTTP1.X不可克服的問題:比如因隊頭阻塞而使傳輸速度受限制。對於資源請求量小的網站性能提升並不明顯。在安全性方面,SPDY建立在TLS之上,URL scheme也是https,這點和HTTP2.0相同。隨着HTTP2.0的定稿很多瀏覽器也都開始拋棄了SPDY,改為支持HTTP2.0,包括Chrome。筆者曾經遇到一個有意思的問題,就是本文中前個段落提到的在webpack-dev-server開啟了HTTPS,增加了請求並發的性能。但在最新版本的FireFox下訪問,webpack-dev-server的進程會報錯,導致npm start進程死掉。看報錯日志是由底層stream包報上來的一直到spy包,也就是瀏覽器端和Server端的傳輸協議不太匹配,導致對在Server端對流的操作失敗了。看了webpack-dev-server的源碼他是用express起的server服務,如果配置項HTTPS被設置為Ture,就生成一些fake的證書之類的,用spdy起服務,否則直接用普通的http模塊起服務。這個腳手架是很久以前的了,它使用的spdy模塊的協議草案版本可能和當前最新的FireFox已經不能適配了。但在Chrome中是OK的,可能是自家支持地比較好。找到一個13年老版本的FirFox,進入參數配置界面,關於SPDY部分有以下參數,可供參考:
對於HTTP2.0、SPDY、HTTPS在實際實施上,有些方面需要注意:
1. HTTP2.0是可以不基於HTTPS的,也就是可以明文傳輸,但是在目前幾大廠商的瀏覽器的實現里面,都是基於TLS來支持HTTP2.0,所以要實施HTTP2.0,必須先部署HTTPS,但這一點不一定一直是正確的,隨服務端的不同部署策略和瀏覽器版本更新,都有可能不同。
PS:這里體現出了協議(標准)和具體應用的實現上的差異。就拿HTTP協議本身來說,協議是無狀態的,但應用在使用協議的時候,可以在不改變協議的前提下,利用協議預留的增強方式增強協議的功能,比如在客戶端請求頭HEADERS引入cookie屬性,在服務端響應頭引入session,搭配使用,多個HTTP請求的請求之間就有狀態了,突破了協議本身,HTTP協議本身並沒有關於Cookie的內容,Cookie最開始是由網景公司推動的,后來各大廠商的瀏覽器也開始支持Cookie,已解決在實際應用中,由無狀態帶來的弊端。所以協議中的描述與協議應用到真實應用上的最終效果,是有差異的。
2. HTTPS不依賴HTTP2.0,可以通過HTTP1.1建立連接,但是后續或切換協議為HTTP2.0。
3. SPDY依賴HTTPS,所以如果使用SPDY模塊啟用服務,需要HTTPS相關的准備。webpack-dev-sever要啟動HTTP2.0支持,也只能通過設置env文件HTTPS=true來通過spdy模塊來啟動Express服務,否則直接通過http模塊啟動。
4. 對於不兼容HTTP2.0的瀏覽器,像Nginx這類服務器,會自動降級為HTTP1.1,以適配瀏覽器端。
這里不再做SPDY的詳細介紹,有興趣的同學可以搜搜相關資料。
五、承前啟后的QUIC
如果你仔細閱讀了上述的內容,你會發現HTTP協議最大的瓶頸其實是在傳輸層TCP/IP協議本身上面,無論TCP/IP上層的應用層協議怎么優化,也無法改變TCP/IP本身過時的設計。所以Google技術推廣部在通過SPDY扶正HTTP2以后,又在推基於傳輸層UDP協議的QUIC協議,有可能這就是未來的HTTP3.0協議,對迎接5G時代的來臨極具意義。具體請看筆者轉載的這篇文章。
六、WebSocket
在實際項目中webscoket協議的出現頻率也是很高的,這里順帶說一句。websocket連接的建立也是先進行TCP握手建立TCP連接,再發起HTTP1.x請求進行握手,最后升級(切換)HTTP協議成websocket協議,進而建立websocket連接:
而且發起websocket連接的客戶端的html靜態文件不需要放在HTTP的服務器下面,可以直接和websocket服務器建立連接。也就是說可以直接通過file://協議在瀏覽器打開一個html文件,在該html內部的<script>腳本里面可以直接請求server端建立websocket連接。
但是按照IETF的websocket協議規范RFC6455,握手請求必須由HTTP協議發出,再進行協議的upgrade,比如:
GET /chat HTTP/1.1
Host: 192.168.12.67:8001
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dFhlIXNhbXBsZSBub22jZM==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
注意:13版本的握手協議和舊版本是有區別的。
what?這個html文件沒有被HTTP服務器serve,是通過file://協議打開的,居然還能發出HTTP請求而且沒有跨域?為什么跨域了呢,因為file://協議訪問是沒有域的,location.host為空。所以后面連着跟了一個/,導致3斜線連在一起了。
從上面的wireshark抓包結果來看,TCP握手成功以后確實是成功發起了HTTP請求的,再升級(切換)HTTP協議成websocket協議的,HTTP的Code為101(Switching Protocols)。這個可能是瀏覽器對websocket API做了特殊處理吧,瀏覽器似乎代為發出了到websocket服務器的HTTP跨域請求用於websocket建立前的握手,或者是說websocket服務器在握手階段是支持HTTP的跨域請求?不管是啥原因,應該都是為了遵守IETF的RFC6455協議規范。
可以看出,websocket協議和HTTP處於平級,都是應用層的協議,並不是websocket是基於HTTP的,或者HTTP是基於websocket的。只是websocket借鑒了HTTP協議的規范用來建立連接,websocket連接一旦通過HTTP協議建立成功后,HTTP協議即被拋棄掉了,后續的數據傳輸都是通過websocket協議了,它的握手可以被HTTP服務器解釋為一個升級請求。因此它們之間有一定交集。並且在傳輸層上都默認依賴TCP/IP協議。
七、HTTP協議與TCP/IP協議之間是什么關系呢
就如同上面提到的一樣,在WEB通信中,HTTP協議默認使用TCP/IP協議作為其在底層依賴的傳輸層協議,當然使用TCP/IP協議並不絕對的,依據協議規范,任何可靠的傳輸層內協議都可以被使用。如果能保證UDP的"可靠",它也可被作為HTTP協議的傳輸層依賴。如果TCP/IP被比喻成發動機或者底盤之類的底層模塊的話,那么HTTP協議就是基於這些底層模塊而構建出來的可以方便使用的具備功能聯合的汽車。通過使用HTTP協議進行網絡通信的時候,我們不需要再關注底層的協議棧,只需要按照HTTP協議的請求-響應的約定進行通信即可。而我們使用XHR(Ajax)相當於在HTTP協議更上層的API封裝,它提供了create, send 以及狀態變化回調的各種功能函數,而不再需要自己從頭摸索HTTP協議。就相當於汽車駕校一樣,我們可以直接學習到一套標准的開車(=.=)流程輕松通過考試,而不需要自己摸索怎么考過關。
好了到此結束吧。
HTTP1.0、HTTP1.1、HTTP2.0之間還有很多的區別,每個版本之間的變化也很大,包括header壓縮、keep-alive優化、二進制格式、多路復用等。本文只是圍繞着實際項目中遇到的一些應用案例進行膚淺的介紹,如果對協議本身感興趣,並渴望深入了解的同學可以直接閱讀HTTP協議本身。