客戶端和服務端的交互
面試題: 當用戶在地址欄中輸入網址,到最后看到頁面,中間都經歷了什么? (引出前后端交互模型的內容)
客戶端 =======> 服務端 (request請求階段)
服務端 <======= 客戶端 (responese響應階段)
1.URL地址解析
2.DNS域名解析(DNS服務器)
3.和服務器建立TCP連接 (三次握手)
4.把客戶端信息傳遞給服務器(發送HTTP請求)
5.服務器得到並處理請求(HTTP響應內容)
6.客戶端渲染服務器返回的內容
7.和服務器端斷開TCP連接(四次揮手)
**下面分別由這7個點來展開說明**
----------------------------------------------------------------
(補充說明)服務器接收到請求后:
1.根據端口號找到對應的項目
2.根據請求資源的路徑名稱找到資源文件
3.讀取資源文件中的內容
4.把內容返回
一.URL地址解析
1.1 URI / URL / URN
URL: Uniform Resource Locator 統一資源定位符 (根據這個地址可以找到一個地方)
URN: Uniform Resource Name 統一資源名稱 (一般看不到)
URI :Uniform Resource Identifier 統一資源標識符,URL和URN是URI的子集
1.2 一個完整的URL所包含的完整的內容
-
協議:http: // (傳輸協議就是,能夠把客戶端和服務器通信的信息,進行傳輸的工具,類似於快遞小哥)
- http超文本傳輸協議
- https更加安全的http,一般涉及支付的網站都要采用https協議(加密傳輸)
- ftp 文件傳輸協議 (一般應用於把本地資源上傳到服務器)
-
域名:www.xiaohuang.cn (不通過域名,直接用服務器的外網ip地址也能訪問,域名就是一個方便記憶的名字)
- 頂級域名 :qq.com (買域名了時候,都是買頂級域名)
- 一級域名:www.qq.com
- 二級域名 :sports.qq.com
-
端口號(:80):端口號的取值范圍0~65535 ,用端口號來區分同一個服務器上的不同項目
- http默認端口號:80
- https默認端口號: 443
- 如果項目采用的就是默認端口號,我們在書寫地址的時候,不用加端口號,瀏覽器發送請求的時候會幫我們默認加上
-
請求資源l路徑名稱:stu/index.html
- 默認的路徑或者名稱(xxx.com/stu/ 不指定資源名,服務器會找默認的資源,一般默認資源名是default.html , index.html)
- 注意偽URL地址的處理:https://item.jd.com/100005297930.html =>其實是 https://item.jd.com/index.php?id=12344 , ( URL重寫技術是為了增加SEO優化的,動態的網址一般不能被搜索引擎優化,所以我們要把動態網址靜態化)
-
問號傳參信息:?form=wx&lx=1
- 客戶端想把信息傳遞給服務器,有很多種方式:1.URL地址問號傳參 2.請求報文傳輸(請求頭和請求主體)
- 也可以不同頁面之間的信息交互,例如:從列表到詳情
-
hash值:#zhenyu
- 也能充當信息傳輸的方式
- 錨點定位
- 基於hash實現路由管控(不同的hash值,展示不同的組件和模塊)
1.3 URL中特殊字符編碼和解碼
請求地址中如果出現非有效UNICODE編碼內容(空格,中文,特殊字符),現代版瀏覽器會默認進行編碼
-
encodeURI / decodeURI
- 基於encodeURI編碼,我們也可以基於decodeURI解碼 (這兩個都是window下的方法,可以直接調用,下面的方法同理)
-
encodeURIComponent / decodeURIComponent
- encodeURIComponent / decodeURIComponent它相對於encodeURI來說,不用於給整個URL編碼,而是給URL部分信息進行編碼(一般都是問號傳值編碼)
-
escape / unescape
- 這種方式一般只用於客戶端頁面之間自己的處理,例如:從列表跳轉到詳情,在比如如果我們在客戶端中的cookie信息,如果是中文,我們也基於這種辦法編碼
-
....
二. DNS服務器域名解析
DNS就是域名解析服務器,這個服務器是歸全世界的(在服務器上存貯着 域名 ,服務器外網ip的相關記錄)
而我們發送請求的時候所謂的DNS解析,其實就是根據域名在DNS服務器上查找對應服務器的外網ip
拿阿里雲舉例:如何在雲服務器上完成域名和外網ip的關聯
進入域名管理界面 => 點擊解析 => 點擊添加記錄 => 1.記錄類型(ipv4就是外網ip)2.主機記錄(www)3.解析路線(默認) 4.記錄值 (服務器的具體ip地址) => 確定 (完成了在DNS域名服務器上添加了一個域名關聯某個ip地址的記錄)
2.0 釣魚網站
我們常見的釣魚網站就是,通過技術手段,模擬假DNS服務器,使用戶訪問某個官方的域名的時候,不是通過DNS服務的解析訪問正常ip地址,而是黑客自身的ip地址,然后記錄釣魚網站用戶輸入的用戶名和密碼
2.1 DNS優化
-
DNS緩存(一般瀏覽器會在第一次解析后,默認建立緩存,事件很短,只有一分鍾左右)
-
<head> <meta charset="UTF-8"> <!-- DNS預獲取 --> <meta http-equiv="x-dns-prefetch-control" content="on"> <link rel="dns-prefetch" href="//static.360buyimg.com"> <link rel="dns-prefetch" href="//misc.360buyimg.com"> </head> DNS預獲取:在頁面加載開始的時候,就把當前頁面中需要訪問其他域名(服務器)的信息進行提前DNS解析,以后加載到具體內容部分就不同解析了
-
-
減少DNS解析次數(一個網站中我們需要發送請求的域名和服務器盡可能少即可)
三.建立TCP連接(三次握手)
用大白話來說:
- 第一次握手:有瀏覽器發起,告訴服務器我要發送請求了
- 第二次握手:有服務器發起,告訴瀏覽器我准備接受了,你趕緊發把
- 第三次握手: 由瀏覽器發送,告訴服務器,我馬上就發了,准備接受把
具體:
- 第一次,瀏覽器發送:SYN=1 seq=J (這個J是自定義也可以是A。。)
- 第二次,服務器返回:SYN=1 ACK=1 ack = J+1 ,seq=K
- 第三次, 瀏覽器發送:ACK=1 ack=K+1
就是通過簡單的三次握手,用代碼的方式,表達能夠相互通信 (就是在真正發送請求之前,臨時驗證能否正常通信,建立好通信通道)
四.發送HTTP請求
就是把需要發送的數據,通過建立的TCP通道,傳輸到服務器 (TCP類比通道,HTTP類比快遞員)
4.1 HTTP請求報文
- 起始行
- 請求頭 Headers(首部)
- 請求主體
4.2 強緩存 和 協商緩存
這部分另外單獨分析
- 強緩存 (Cache-Control 和 Expires)
- 協商緩存 (Last - Modified 和 Etag)
五.服務器響應HTTP內容
一般來說,web服務器和數據服務器在同一個服務器的相同服務下(協議,域名,端口都一致),這種就是同源策略請求 =>AJAX , 但是在真實項目中往往兩台服務器是分開的,此時是非同源策略請求 =>跨域
5.1 WEB(圖片)服務器和數據服務器
- Tomcat
- Nginx
- Apache
- IIS
5.2 HTTP響應報文
2XX 成功:響應代碼表明操作成功了。
3XX 重定向:客戶端需要做些額外工作才能得到所需要的資源。它們通常用於GET請求。他們通常告訴客戶端需要向另一個URI發送GET請求,才能得到所需的表示。那個URI就包含在Location響應報頭里
4XX 客戶端錯誤:這些響應代碼表明客戶端出現錯誤。不是認證信息有問題,就是表示格式或HTTP庫本身有問題。客戶端需要自行改正。
5XX 服務端錯誤: 這些代碼意味着服務器處於不能執行客戶端請求的狀態,此時客戶端應稍后重試。有時,服務器能夠估計客戶端應在多久之后重試。並把該信息放在Retry-After響應報頭里。
- 響應狀態碼
- 200 ok: 成功
- 201 created: 一般應用於告訴服務器創建一個新文件,最后服務器創建成功后返回的狀態碼
- 204 no content:對於某些請求(例如:put或者delete),服務器不想處理,可以返回空內容,並且用204狀態碼告知
- 301 :永久重定向(永久轉移) 讓客戶端使用另外一個url來請求資源
- 302 :臨時轉移,很早以前基本上用302來做,但是現在主要用307來處理這個事情,307的意思就是臨時重定向=> 主要用於:服務器的負載均衡 等
- 304 :設置HTTP的協商緩存
- 400 :傳遞給服務器的參數錯誤
- 401:無權限訪問
- 404: 請求地址錯誤
- 500:未知服務器錯誤
- 503:服務器超負荷
- 響應頭(首部)
- 響應主體
六.客戶端渲染頁面
瀏覽器渲染頁面的時候,遇到link / img /audio / video 等是異步去加載資源信息 (瀏覽器分配一個新的線程去加載,主線程繼續向下渲染頁面),如果遇到script或者@import,則讓主線程去加載資源信息 (同步),加載完成信息之后,在去渲染頁面
6.1 瀏覽器渲染頁面的步驟
- 解析HTML,生成DOM樹,解析CSS,生成CSSOM樹
- 將DOM樹結合CSSOM樹結合,生成渲染樹(Render Tree)
- Layout(回流):根據生成的渲染樹,計算它們在設備視口(viewport)內的確切位置和大小,這個階段是回流
- Painting(重繪): 根據渲染以及回流得到的幾何信息,得到節點的絕對像素
- Display:將像素發送給GPU, 展示到頁面上
6.2DOM的重繪與回流
- 重繪:元素樣式(顏色)的改變(但寬高,大小,位置不變)
- 回流:元素的大小或者位置發生了變化(當頁面布局和幾何信息發生變化的時候),觸發了重新布局,導致渲染樹重新計算布局和渲染
- 注意:回流一定會觸發重繪,而重繪不一定會回流
6.3 前端性能優化之:避免DOM的回流
- 放棄傳統操作dom的時代,基於vue/react開始數據影響視圖模式(因為操作dom必然是會觸發回流)
- 分離讀寫操作(現代的瀏覽器都有渲染隊列的機制)
- =>現代瀏覽器都有"隊列機制":發現某一行要修改元素的樣式,不立即渲染,而是看看下一行,如果下一行也會改變樣式,則把修改樣式的操作放到"渲染隊列中"...一直到不是修改樣式后的操作后,整體渲染一次,引發一次回流
- 當我們獲取一些樣式的時候,如box.offsetWidth(偏移量,盒子寬度等),會刷新渲染隊列,所以我們在設置樣式和獲取樣式的時候,應該集中書寫,而不是交叉書寫,做到讀寫分離
- 樣式集中改變
- 緩存布局信息
- 當我們需要多次修改某個屬性的時候,不應該獲取到了之后直接修改,應該用一個變量存貯獲取到的值,再來進行修改 (原理就是讀寫分離)
- 元素批量修改
- 案例說明:當我們需要往一個盒子中插入10個盒子的時候,錯誤:循環10次插入(引發回流重繪),正確:使用模板字符串,循環10次,將所有盒子集中到一個字符串中,一次性插入到dom樹當中
- 使用文檔碎片(存儲文檔的容器),先插入到文檔碎片中,最后將文檔碎片插入到頁面當中
- 動畫效果應用到position屬性為absolute或fixed元素上(脫離文檔流)
- css3硬件加速(GPU加速)
- 犧牲平滑度換取速度
- 避免table布局和使用css的javascript表達式
七. 斷開連接(四次揮手)
四次揮手,是發生在信息的傳輸過程中,三次揮手,是發生在信息傳出之前的
- 第一次揮手:由瀏覽器發起,發送給服務器,我請求報文發送完了,你准備關閉把
- 第二次揮手:由服務器發起,告訴瀏覽器,我接受完請求報文了,我准備關閉,你也准備把
- 第三次揮手:由服務器發起,告訴瀏覽器,我響應報文發送完畢,你准備關閉把
- 第四次揮手:由瀏覽器發起,告訴服務器,我響應報文接受完畢,我准備關閉,你也准備把
真實傳輸:
- 第一次:瀏覽器發出 FIN M
- 第二次: 服務器發出 ack M+1
- 第三次: 服務器再次發出 FIN K
- 第四次:瀏覽器發出 ACK=1 ack=K+1