HTTP详解(更新完结)
?
一、網絡協議基礎
osi中模型有七層,分別是物理層,數據鏈路層,網絡層,傳輸層,會話層,表示層,應用層
五層的網絡協議中有,物理層,數據鏈路層,網絡層,運輸層,應用層
?
?
?
?
2、其中各層的作用如下
(1)物理層,就是定義物理設備的標準,比如網線的接口類型沒傳輸介質的傳輸速率等,主要是傳輸比特流,就是(將1,0轉換為電流強弱來進行傳輸,到達目的地后就轉化為1,0,也就是數模轉換和模數轉換),傳輸的數據是比特。(其實就是網線那里的協議)
?
(2)數據鏈路層,我的理解就是從物理層傳輸過來的數據封裝成幀(在數據前后添加首部和尾部),主要作用在交換機和網橋,這里有三個很重要的問題就是封裝成幀,透明傳輸以及差錯檢測。
其中封裝成幀就是接受端在收到物理層上交的比特流之后,能根據首部和尾部的標記,從收到的比特流中識別幀的開始于結束,然后發送出去
而透明傳輸就是只要有數據就傳出去,保證了數據一定會被傳輸出去,也就是說不管什么樣的字符都可以放在這樣的幀傳輸過去。
差錯就測就是檢測幀里面的比特哪里有錯,并且把它進行修改。
?
(3)網路層也即是我們ip協議作用的地方了。在不同地理位置的網絡中的兩個主機系統之間提供鏈接和路徑的選擇(就是當交換機把數據發送出去,到了網絡成之間,根據兩個主機的地址而選擇不同的網絡路由)
?
(4)傳輸層,定義了傳輸數據的協議和端口號,就是之前ping端口是不對的,因為端口它是在傳輸層,Tcp,Udp,也是作用在這里,就是將下層接收過來的數據進行分段和傳輸,到達目的地址再進行重組,這一層的數據叫段。(tcp三次握手四次揮手大家都懂)
?
(5.)會話層,通過傳輸層(端口號:傳輸端口與接收端口)建立數據傳輸的通路,主要在系統之間發起或者接收會話請求
(6)表示層:確保一個系統的應用層發送的信息可以被另一個系統的應用層讀取
(7)應用層:報文,也即是兩個軟件之間的通信。
?
所以普通五層協議以及TCP\IP中的應用層就包含了應用層、表示層、會話層,所以比如郵件服務(SMTP),文件傳輸(FTP)和終端仿真(Telnet)等都是在這里
?
?
3、ip,mac,arp
IP地址指明了節點被分配到的地址,MAC地址是指網卡所屬的固定地址。IP地址可以和MAC地址進行配對,IP地址可變換,但MAC地址基本上不會更改。
?
利用ARP協議憑借MAC地址進行通信。
IP之間的通信依賴MAC地址。在網絡上,通信雙方在同一局域網內的情況很少,通常是經過多態計算機和網絡設備中專才能連接到對方。而在進行中轉事,會利用下一站中轉設備的MAC地址來搜索CIA一個中轉目標。這是,會采用ARP協議。ARP是一種用來解析地址的協議,可以根據IP地址查出對應的MAC地址,
?
?
?
?
4、TCP
按層次分,TCP位于傳輸層,提供可靠的字節流服務。
字節流服務就是為了方便傳輸,將大塊的數據分隔以報文段為單位的數據包進行管理。而可靠的傳輸服務是指,能夠把數據準確可靠地傳給對方。也就是說,TCP是為了更容易的傳輸大數據才把數據進行分隔。
?
另一方面,TCP為了能夠確保數據能夠送達對方,采用了三次握手。用TCP協議把數據包傳輸出去后,TCP一定會向對方確認是否成功送達,這個過程使用了兩個flag,分別是SYN,ACK。
發送算首先發送一個帶SYN標志的數據包給對方,接收端收到后,回傳一個帶有SYN/ACK標志的數據包以示傳達確認信息,最后發送端再回傳一個帶ACK標志的數據包,代表握手結束。
?
5、DNS,位于應用層的域名解析服務,提供域名和IP地址之間的解析服務。
?
?
二、http基礎
?
?
1、http是建立在tcp\ip協議的基礎上的。所以這里的層級采用的四層,分別是應用層,傳輸層,網絡層以及接口層。
所以當一個客戶端發起請求的時候,主要順序如下
-
首先作為發送端的客戶端在應用層(http協議)發出一個想要某個web頁面的http請求。
-
然后為了傳輸方便,在傳輸層(TCP)協議把從應用層收到的數據進行分割后在各個報文上打上標記序號以及端口號轉發給網絡層
-
在網絡層中 鏈路層,這樣發送網絡的請求就準備好了
-
接受端的服務器在鏈路層中接收到數據,按序往上層發送,一直到應用層,當傳輸到應用層的時候,才能真正接受到由客戶端發送過來的HTTP請求。
?
?
?
2、URI和URL的區別
URI是uniform resource identifier的縮寫,也就是統一資源標識符,就是由某個協議方案表示的資源的定位標識符
URL是資源所在的地址,所以URL是URI的子集。
URI不一定是URL,但URL一定是URI
?
3、簡單的http協議
(1)http協議是用于客戶端和服務器之間的通訊,http協議規定,請求從客戶端發出,最后服務器響應該請求并返回,也就是說,一定是客戶端先發起通信的。
?
(2)在請求頭中
GET /index.html HTTP/1.1
Host:banma.com
?
上面中的第一行指定了請求的方法、請求的資源、http的版本號
這一行的意思就是,請求某臺http服務器上的/index.html頁面資源
?
而請求報文中,是由請求方法、請求URI、協議版本、可選的請求首部字段和內容實體構成的
前三者是第一行,后面的是請求首部,再然后是內容實體,比如
GET /index.html HTTP/1.1
Host:banma.com
Connection:keep-alive
Content-Type:application/x-www-form-urlencoded
Content-Length:16
?
?
name=krysliang&age=23
?
?
在上面的代碼中,2-5行是請求首部字段,最后一行是內容實體
?
接收到這樣的請求后服務器會返回類似于下面這樣的response
HTTP/1.1 200 OK
Date:Tue,21 May 2019 15:57:16 GMT
Content-Length:362
Content-Type:text/html
?
<html>…..
?
在第一行中表明了http的版本號,狀態碼和原因
2-4行中顯示了創建響應的日期時間,還有首部的一些字段
最后的是響應的實體部分,也叫body
?
(3)http是不保存狀態的協議,http協議自身不對請求和響應之間的通信狀態進行保存,也就是說在http這個級別,協議對于發送過的請求或響應都不做持久化處理。換句話說,就是一個request對應一個response,每次有新的請求發送的時候,就會有對應的新響應產生,這是為了更快的處理大量事務,而把HTTP設計成這么簡單的,
?
(4)URI
HTTP協議使用URI定位互聯網上的資源。正式因為URI的特定功能,在互聯網上任意位置的資源都能訪問到。
?
?
?
請求URI的方式可以有多種,比如
?
?
如果不是訪問特定資源,而是對服務器本身發起請求,可以用一個*來代替請求URI。
?
(5)METHOD
-
GET獲取資源
GET方法用來請求訪問資源,指定的資源經過服務器解析后返回響應內容,也就是說,如果GET請求的資源是文本,那么將保持原樣返回,如果是程序,那么將返回經過執行后的輸出結果。
?
| 請求 | GET /index.html HTTP/1.1 |
| 響應 | 返回index.html的頁面資源 |
| 請求 | GET /index.html HTTP/1.1 Host: www.krysliang.com If-Modified-Since: Thu, 12 Jul 2019 07:30:00 GMT |
| 響應 | 僅返回2019年7 月12日7 點30分以后更新過的index.html頁面資源。如果未 有內容更新,則以狀態碼304 Not Modified作為響應返回 |
?
-
POST?傳輸實體
POST用來傳輸實體的主體,也就是說可以傳輸form表單等數據給服務器,雖然GET也可以傳輸實體的主體,但一般不用GET方法進行傳輸。
?
| 請求 | POST /submit.cgi HTTP/1.1 Host:www.krysliang.com Content-Length:1560 |
| 響應 | 返回submit.cgi接收數據的處理結果 |
-
PUT?傳輸文件
PUT方法用來傳輸文件。在請求主體中包含文件的內容,然后保存到請求URI指定的位置。一般不用這個方法來上傳文件,因為這個方法不帶驗證功能,所以會造成安全性的問題。
?
?
| 請求 | PUT /example.html HTTP/1.1 Host:www.krysliang.com Content-Type: text/html Content-Length: 1560(1560 字節的數據) |
| 響應? | 響應返回狀態碼 204 No Content(比如 :該 html 已存在于服務器上) 204是請求執行成功了,但無數據返回 |
?
-
HEAD?獲得報文首部
HEAD和GET方法一樣,但是不返回報文主體部分,用來確認URI的有效性及資源更新的日期時間等信息。
?
| 請求 | HEAD /index.html HTTP/1.1 Host:www.krysliang.com |
| 響應 | 返回index.html有關的響應首部 |
?
-
DELETE?刪除文件
DELETE方法按請求URI刪除指定的資源。跟PUT有點像,也是因為安全性不好,一般不使用
-
OPTIONS:詢問支持的方法
OPTIONS用來查詢指定URI資源所支持的方法
?
| 請求 | OPTIONS *?HTTP/1.1 Host:www.krysliang.com |
| 響應 | HTTP/1.1 200?OK Allow:GET,POST,HEAD,OPTIONS |
?
-
TRACE:追蹤路徑
將客戶端請求到服務器這個過程的通信環返回給客戶端。
原理:發送請求的時候,再Max-Forwards首部字段中填入數值,每經過一個服務器端就將該數字減1,當數值剛好減到0的時候,則停止傳輸,最后收到請求的服務器則返回狀態嗎200?OK的響應。
但是這個方法容易引發跨站追擊的攻擊。一般都不要使用。
?
| 請求 | TRACE / HTTP/1.1 Host:?www.krysliang.com Max-Forwards: 2 |
| 響應 | HTTP/1.1 200 OK Content-Type: message/http Content-Length: 1024 TRACE / HTTP/1.1 Host:??www.krysliang.com Max-Forwards: 2(返回響應包含請求內容 |
?
-
CONNECT:要求使用隧道協議連接代理
CONNECT方法要求在與代理服務器通信的時候建立隧道,實現用隧道協議進行TCP通信。主要使用SSL(安全套接字)和TLS(傳輸層安全)協議把通信內容加密后經網絡隧道傳輸
格式如下
?
CONNECT?代理服務器名:端口號?HTTP版本
?
?
詳細的可以看這個博客https://www.joji.me/zh-cn/blog/the-http-connect-tunnel/
?
(6)持久化連接
http協議初始版本中,每進行一次http通信就要斷開一次TCP連接
?
這樣的話,當傳輸大量的資源的時候就會造成無畏的TCP連接建立和斷開,增加通信量的開銷。
?
持久化的意思就是,只要任意一段沒有明確提出斷開連接,則保持TCP連接狀態。這樣就減少了TCP連接的重復建立和斷開所造成的額外開銷,減輕了服務器的負載。
?
此外,使用持久連接還有另一個好處,就是可以使用管線化的方式來進行發送
什么?什么是管線化?
來。
管線化的意思就是,只要建立起了TCP連接,一個HTTP請求不用等到對應的響應再開始發送下一個HTTP請求。一個管線一個請求或者響應。比如,當請求一個包含10張圖片的HTML頁面,管線化就可以一下子請求10個圖片以及一個html頁面。
看圖
?
?
(7)cookie
因為http是無狀態協議,也就是說它不對之前發生過的請求和響應的狀態進行管理,也就是說,無法根據之前的狀態進行本次的請求處理。假設要求登錄認證的頁面本身無法進行胡葬臺的管理,那么每次跳轉新的頁面就要再次登錄,就是要在每次請求報文中附加參數來管理登錄狀態,這無疑又給服務器增加了新的壓力。于是引入了cookie,cookie通過請求和響應報文中寫入cookie信息來控制客戶端的狀態。
?
?
?
服務端發送回來的響應報文中有一個Set-Cookie的首部字段信息,通知客戶端保存Cookie。當下次客戶端再往服務端發送請求的時候,客戶端將自動在請求報文中加入cookie值后再發送出去。
服務端發現客戶端發送過來的Cookie后,會去檢查究竟是從哪一個客戶端發過來的連接請求,然后對比服務端上的記錄,最后得到之前的狀態信息。
?
?
?
?
1. 請求報文(沒有 Cookie 信息的狀態)
GET /reader/ HTTP/1.1
Host: www.krysliang.com
*首部字段內沒有Cookie的相關信息
2. 響應報文(服務器端生成 Cookie 信息)
HTTP/1.1 200 OK
Date: Thu, 12 Jul 2012 07:12:20 GMT
Server: Apache
<Set-Cookie: sid=1342077140226724; path=/; expires=Wed,
10-Oct-12 07:12:20 GMT>
Content-Type: text/plain; charset=UTF-8
?
?
3. 請求報文(自動發送保存著的 Cookie 信息)
GET /image/ HTTP/1.1
Host: www.krysliang.com
Cookie: sid=1342077140226724
?
三、HTTP里面的信息
1、什么是HTTP報文
用于HTTP協議交互的信息叫做HTTP報文。請求端的叫做請求報文,響應端的叫做響應報文,
HTTP報文本身是由多行數據構成的字符串文本。
其分為兩部分,分別是首部和主體,兩者用空行分隔開,一個報文中不一定有注意。
?
?
2、報文首部格式
(1)HTTP請求報文首部
在請求報文中,HTTP報文由方法、URI、HTTP版本、HTTP首部字段等構成。
?
如:
GET / HTTP/1.1
Host: www.krysliang.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*; q=0.8
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
If-Modified-Since: Fri, 31 Aug 2007 02:02:20 GMT
If-None-Match: "45bae1-16a-46d776ac"
Cache-Control: max-age=0
?
(2)HTTP響應報文首部
響應報文首部由HTTP版本、狀態碼、HTTP首部字段3部分構成。
?
?
如:
HTTP/1.1 304 Not Modified
Date: Thu, 07 Jun 2012 07:21:36 GMT
Server: Apache
Connection: close
Etag: "45bae1-16a-46d776ac"
?
(3)HTTP首部字段
從上面可以看出,無論是請求報文還是響應報文,都有HTTP首部字段,可以看得出它蠻重要的。首部字段是給瀏覽器和服務器提供報文主體大小、所使用的語言等內容。
?
?
-
首部字段結構:HTTP首部字段是由首部字段名和字段值構成,中間用冒號":"分隔,如
Content——Type:text/html
此外字段可以擁有多個值,如
Keep-Alive: timeout=15, max=100
?
-
4種HTTP首部字段類型
HTTP首部字段分為以下幾種:通用首部字段(也就是請求報文和響應報文兩方都會使用的首部),請求首部字段,響應首部字段,實體首部字段
-
首部字段
?? ??? ?通用首部字段
?
| 首部字段名 | 說明 |
| Cache-Control | 控制緩存的行為 |
| Connection | 逐跳首部、鏈接的管理 |
| Date | 創建報文的日期時間 |
| Pragma | 報文指令 |
| Trailer | 報文末端的首部一覽 |
| Transfer-Encoding | 指定報文主體的傳輸編碼方式 |
| Upgrade | 升級為其他協議 |
| Via | 代理服務器的相關信息 |
| Warning | 錯誤通知 |
?? ?請求首部字段
?
| 首部字段名 | 說明 |
| Accept | 用戶代理可處理的媒體類型 |
| Accept-Charset | 優先的字符集 |
| Accept-Encoding | 優先的內容編碼 |
| Accept-Language | 優先的語言 |
| Authorization | web認證信息 |
| Expect | 期待服務器的特定行為 |
| From | 用戶的電子郵箱地址 |
| Host | 請求資源所在服務器 |
| If-Match | 比較實體標記(ETag) |
| If-Modified-since | 比較資源的更新時間 |
| If-Unmodified-Since | 比較資源的更新時間 |
| Max-Forwards | 最大傳輸逐跳數 |
| Proxy-Authorization | 代理服務器要求客戶端的認證信息 |
| Range | 實體的字節范圍請求 |
| Referer | 對請求中URI的原始獲取方 |
| TE | 傳輸編碼的優先級 |
| User-Agent | HTTP客戶端程序的信息 |
?
?
?? ?響應首部字段
?? ??? ?
?
| 首部字段名 | 說明 |
| Accept_Ranges | 是否接受字節范圍請求 |
| Age | 當代理服務器用自己緩存的實體去響應請求時,用該頭部表明該實體從產生到現在經過多長時間了(也就是距離第一次問服務器請求獲得資源的時間) |
| ETage | 資源的匹配信息 |
| Location | 令客戶端重定向至指定URI |
| Proxy-Authenticate | 代理服務器對客戶端的認證信息 |
| Retry-After | 對再次發起請求的時機要求 |
| Server | HTTP服務器的安裝信息 |
| Vary | 代理服務器緩存的管理信息 |
| WWW-Authenticate | 服務器對客戶端的認證信息 |
?
?? ?實體首部字段
?
| 首部字段名 | 說明 |
| Allow | 資源可支持的HTTP方法 |
| Content_Encoding | 實體主體適用的編碼方式 |
| Content-Language | 實體主體的自然語言 |
| Content-Location | 替代對應資源的URI |
| Content-Length | 實體主體的大小(字節) |
| Content-MD5 | 實體主體的報文摘要 |
| Content-Type | 實體主體的媒體類型 |
| Expires | 實體主體過期的日期時間 |
| Last-Modified | 資源的最后修改日期時間 |
?
?
?
?
?
?
?
?
?
3、HTTP中的編碼
HTTP在傳輸數據的時候開業按照數據原貌直接傳輸,也可以在傳輸過程匯總通過編碼提升傳輸速率。通過在傳輸時編碼可以有效的處理大量的訪問請求,但是編碼的操作需要計算機來完成,因此會消耗更多的CPU資源。
(1)壓縮傳輸的內容編碼,這個編碼方式有gzip、compress、deflate、identity(不進行編碼)
壓縮內容的意思就是服務器端將實體進行壓縮編碼,然后再客戶端進行解碼。
?
(2)分隔發送的分塊傳輸編碼
如果在請求的編碼實體資源尚未全部傳輸完成之前,瀏覽器將無法顯示請求頁面,那么在傳輸大容量數據的時候,通過把數據分隔成多塊,能夠讓瀏覽器逐步顯示頁面。換句話說,分塊傳輸編碼就人如其名,是將資源進行分塊然后傳輸的編碼。
詳細的可以看這里https://foofish.net/http-transfer-encoding.html
我的理解就是,TCP不是管線化嘛,然后HTTP也是持久連接嘛,所以當一個請求頭部?指定了Transfer-Encoding: chunked的時候,那么這個request對應的response將會分成多個塊回傳給客戶端,客戶端拿到一個就先顯示一個,然后遇到最后一個長度為0的response就等于這個request對應的response傳輸結束了。
?
?
?
?
4、狀態碼
狀態碼就是描述返回的請求結果,根據狀態碼可以判斷服務器處理請求的結果。
(1)狀態碼的組成:3位數字和原因短語,如果200 OK
(2)狀態碼的分類
?
| ? | 類別 | 原因短語 |
| 1XX | Information(信息) | 接收的請求正在處理 |
| 2XX | Success(成功) | 請求正常處理 |
| 3XX | RedirectCtion(重定向) | 需要進行附加操作以完成請求 |
| 4XX | Client?Error(客戶端錯誤) | 服務器無法處理請求 |
| 5XX | Server?Error(服務器錯誤) | 服務器處理請求出錯 |
?
-
2XX
?
1)200 OK
這個狀態碼表示請求被正常處理了。在響應報文中,隨狀態碼一起返回的信息(body)會因方法的不同而發生改變。比如,使用GET的方法,對應請求資源的實體會作為響應返回,而HEAD則不會返回
?
2)204?NO?Content
該狀態碼表示服務器接收的請求已經成功處理了,但是返回的response中不包含body部分,也就是沒有主題部分。一般發生在只需要從客戶端給服務器發送信息,但對客戶端?不需要發送新信息內容的情況下使用
?
3)206?Partial?Content
該狀態碼表示客戶端進行了范圍請求,而服務器成功執行了這部分的GET請求。response中包含Content-Range指定范圍的實體內容(字節范圍)。
?
?
-
3XX?重定向
3XX的狀態碼表示瀏覽器需要執行某些特殊的處理然后才能正確的處理請求。
1)301?Moved?Permanently?永久性重定向
該狀態碼表示請求的資源已經被分配了新的URI,以后應使用資源現在所指向的URI。也就是說,如果已經把資源對應的URI保存為書簽了,這是應該按首部字段Location提示的URI重新保存
?
2)302?Found?臨時性重定向
改狀態碼表示請求的資源已被分配了新的URI,希望用戶(本次)能使用新的URI訪問
?
3)303?See?Other
該狀態碼表示由于請求對應的資源存在著另一個URI,應使用GET方法定向獲取請求的資源。什么意思呢?就是說,希望你用另一個URI然后GET去請求它。
?
那么301,302,303之間有什么區別呢?
301就是永久重定向,以后該資源都會使用該重定向的URI,302就是臨時,以后這個URI還是有可能會發生變化的,303跟302有點像,但是303明確的表示客戶端應該使用GET來獲取資源。
比如說,當使用POST方法來訪問一個程序,其執行后的結果是希望客戶端能以GET方法重定向到另一個URI上去,則返回303。
?
當301,302,303出現的時候,大部分的瀏覽器都會將POST改成GET,并刪除請求報文內的body,之后請求會自動再次發送。
?
?
4)304?Not?Modifiled
之前使用vue來構建一個管理系統的時候,就出現過這個狀態碼,后來發現是由于webpack的原因。哭唧唧。
該狀態碼表示客戶端發送附帶請求的時候( GET 方法的請求報文中包含 If-Match,If-Modified-
Since,If-None-Match,If-Range,If-Unmodified-Since 中任一首部。),服務器端允許請求訪問資源,但未滿足條件的情況,則返回該狀態碼,response中不包含任何body。
?
5)307?Temporary?Redirect?臨時重定向
跟302有點像,不同的是,307出現的時候不會講POST改成GET,而是遵守標準。
?
?
-
4XX?客戶端錯誤
1)400?Bad?Request
該狀態碼表示request中存在語法錯誤,需要修改了錯誤后再次發送。
?
2)401 Unauthorized?未認證
該狀態碼表示,訪問服務器需要認證或者認證失敗。
出現該錯誤的response中必須含有一個適用于請求資源的www-authenticate的首部,用來驗證用戶的信息。
?
4)403?Forbidden
該狀態碼表示,服務器拒絕訪問該資源。
想看到詳細的理由,如果服務器有給出的話,可以在response中的body部分看到。
?
5)404?Not?Found
該狀態碼表示沒有此資源。
?
-
5XX?服務器錯誤
1)500?Internal?Server?Error
該狀態碼表示服務器在執行請求的時候發生了錯誤。
?
2)503?Service?Unavailable
該狀態碼表示服務器暫時處于超負載或者正在進行停機維護,現在無法處理請求。
?
?
?
四、一些跟HTTP相關的網絡知識
1、虛擬主機
虛擬主機就是,幾個不同的域名其實指向著同一臺服務器,也就是說,一臺服務器可以運行各自不同的網站。
但是虛擬主機不是云服務器,云服務器是群服務器中的一臺。
?
所以當一個服務器中托管了兩個不同的域名,當收到請求的時候就需要搞清楚究竟要訪問哪個域名了。所以在發送HTTP請求的時候,必須在HOST首部內完整指出主機名或域名的URI。
?
2、在客戶端與服務器之間通信的時候,中間會有一些用來轉發數據的機器和對應的應用程序,比如代理、網關、隧道等。
這些應用程序和服務器可以將請求轉達給通信線路上的下一站服務器,并且能接受從那臺服務器發送滾動響應再轉發給客戶端。
?
3、代理
代理是一種有轉發功能的應用程序,是客戶端和服務器之間的中間人,接受由客戶端發送的請求并轉發給服務器,同時,也接收服務器返回的響應并轉發給客戶端。
?
?
?
?
在HTTP通信的過程中,可以級聯多臺代理服務器。請求和響應的轉發會經過書臺類似鎖鏈一樣連接起來的代理服務器。轉發時,需要附加Via首部字段以標記處經過的主機信息。
?
-
緩存代理:代理轉發響應的時候,緩存代理會預先將資源的副本保存在代理服務器上,當代理再次接收到對相同資源的請求時,就可以不從源服務器那里獲得資源,而是將之前緩存的資源作為響應返回。
?
?
緩存是具有有效期的,
?
?
?
?
?
?
?
?
-
透明代理 :轉發請求或響應的時候,不對報文做任何加工。
?
-
非透明代理:轉發請求或響應的時候,對報文內容進行加工
?
4、網關
網關是轉發其他服務器通信數據的服務器,接收從客戶端發送來的請求時,就像自己擁有資源的源服務器一樣對請求進行處理。
?
?
?
?
?
?
5、隧道
隧道的目的是確保客戶端能與服務器進行安全的通信,隧道本身不會去解析HTTP請求。也就是說,請求保持原樣中轉給之后的服務器,隧道會在通信雙方斷開連接時結束。
?
?
?
五、首部字段詳解
(一)通用首部字段
1、Cache-Control
?
這個首部字段是用來控制緩存的,其還包含很多子指令,多個子指令之間用逗號分開,用來控制緩存的工作機制。
其中子指令還可以分為請求和響應兩個部分
(1)緩存請求指令
?
| 指令 | 參數 | 說明 |
| no-cache | 無 | 強制向源服務器再次驗證(也就是說我不想要代理服務器的緩存內容,我想要服務器中最新的資源) |
| no-store | 無 | 不希望代理服務器去緩存請求或者響應的任何內容 |
| max-age=[] | 必須,單位為秒 | 能夠接受已經過去多久的緩存 |
| min-fresh=[] | 必須,單位為秒 | 要求緩存服務器返回至少還沒過指定時間的緩存時間,也就是說,比如指定該值為60,超過了60秒的資源都沒有辦法作為響應返回了。 |
| max-stale([]) | 可省略,單位為秒 | 可接受過期多久的響應,如果指令中未指定參數,那么無論過了多久,客戶端都會接受想用;如果指令中指定了具體數值,那么即使過期,只要仍處于指定數值內,仍舊會被客戶端接收。 |
| no-transform | 無 | 代理服務器不可以對響應實體作任何處理和改變 |
| only-if-cached | 無 | 從緩存中獲取資源,也就是說只要緩存服務器中的資源,如果有則返回,如果沒有則返回504的狀態碼。 |
?
?
(2)響應中跟緩存相關的指令
?
| 指令 | 參數 | 說明 |
| public | 無 | 任何請求都可以接受響應的緩存 |
| private | 可省略 | 僅向特定的用戶返回響應 |
| no-cache | 可省略 | 緩存前必須要確定其有效性 |
| no-store | 無 | 不緩存請求或響應的任何內容 |
| no-transform | 無 | 代理服務器不可更改媒體類型 |
| must-revalidate | 無 | 可緩存但必須再向源服務器進行確認 |
| proxy-revalidate | 無 | 要求代理服務器對緩存的響應有效性再進行確認 |
| max-age=[] | 必須,單位是秒 | 響應的最大的age值,也就是說如果Age已經超過這個值,就要向源服務器再次請求資源 |
| s-maxage=[] | 必須,單位是秒 | 公共緩存服務器響應的最大Age值,跟上面的max-age相同,不同的是這里的代理服務器應為供多位用戶使用的公共緩存服務器 |
?
?
(3)no-cache
no-cache在request和response中的行為是不一樣的,在request中代表客戶端希望直接獲取源服務器的資源,因此代理服務器必須將request轉發給源服務器。在response中代表服務器不允許代理服務器進行緩存
?
?
如果response中的no-cache有參數,代表,客戶端在接收到這個被指定參數值的首部字段對應的響應報文后,就不能使用緩存,無參數的時候,代理服務器可以進行緩存,但是每次使用需要跟源服務器進行確認。
?
(4)max-age,s-maxage
Cache-Control: max-age=604800(單位:秒)
在request中的max-age中帶包,如果資源的緩存時間比指定的時間數值更小,那么客戶端就接收緩存的資源,當max-age為0的時候,緩存服務器通常需要將請求轉發給源服務器。
?
在response中的max-age,在指定的時間內無需再向源服務器進行有效性確認。
?
?
?
2、Connection
這個通用首部字段有兩個作用
(1)管理持久化連接
因為HTTP/1.1之后的都會默認連接時持久化連接,所以當服務器想要明確斷開連接的時候,則指定Connection首部字段的值為close,而在HTTP/1.1之前的都會默認為是非持久連接,所以如果想在舊版本上面維持持久連接則將該字段設置為Keep-Alive
Connection:close
Connection:Keep-Alive
?
(2)控制不再轉發給代理的首部字段
?
?
?
3、Date
這個字段表明創建HTTP報文的日期和時間
Date: Tue, 03 Jul 2012 04:40:59 GMT
?
4、Pragma
這個字段是HTTP/1.1之前遺留下來的,其值為指令
如
Pragma: no-cache
?
5、Trailer
這個字段的作用就是事先說明報文主體之后記錄了哪些首部字段。
?
?
HTTP/1.1 200 OK
Date: Tue, 03 Jul 2012 04:40:56 GMT
Content-Type: text/html
...
Transfer-Encoding: chunked
Trailer: Expires
...(報文主體)...
0
Expires: Tue, 28 Sep 2004 23:59:59 GMT
?
6、Transfer-Encoding
?
?
?
?
7、Upgrade
這個字段用于檢測HTTP協議及其他協議時候可以使用更高的版本進行通信,其參數值可以用來指定一個完全不同的通信協議
?
?
?
需要注意的是此處的Connection是Upgrade,因為Upgrade這個字段產生作用的Upgrade對象僅限于客戶端和鄰接服務器之間,因此為了保證整個通信鏈的暢通,需要在Connection中指定Upgrade
?
8、Via
這個字段是用來追蹤客戶端與服務器之間的請求和響應報文的傳輸路徑。
報文經過代理或網關的時候,會先在首部字段Via中附加該服務器的地址等信息,然后再進行轉發。
?
?
?
在上面的例子中,經過代理服務器A的時候,,Via 首部附加了“1.0
gw.hackr.jp (Squid/3.1)”這樣的字符串值。行頭的 1.0 是指接收請求的服務器上的HTTP的版本,之后的B也是這樣
?
9、Warning中的一些警告碼
?
?
| 警告碼 | 警告內容 | 說明 |
| 110 | Response is stale(響應已過期 | 代理返回已過期的資源 |
| 111 | Revalidation failed(再驗證失敗) | 代理再驗證資源有效性時失敗(服務 器無法到達等原因 |
| 112 | Disconnection operation(斷開連接操 作 | 代理與互聯網連接被故意切斷 |
| 113 | Heuristic expiration(試探性過期 | 響應的使用期超過24小時(有效緩存 的設定時間大于24小時的情況下) |
| 199 | Miscellaneous warning(雜項警告) | 任意的警告內容 |
| 214 | Transformation applied(使用了轉換 | 代理對內容編碼或媒體類型等執行了 某些處理時 |
| 299 | Miscellaneous persistent warning(持久 雜項警告 | 任意的警告內容 |
?
(二)、請求首部字段
1、Authorization
這個字段是用來告知服務器,用戶代理的認證信息,
?
2、Expect
這個字段用來告知服務器,期望其作出的特定行為
3、From
用來告知服務器,使用用戶代理的用戶的電子郵件地址。使用代理是,應盡可能的包含次字段。
4、Host
請求資源的主機名和端口號。
這個字段蠻重要的,比如,當我們地址請求一個部署了多個域名的服務器的時候,經過dns解析之后到達的已經是同一個ip的服務器了,此時如果沒有設置Host,服務器將無法知道請求的是哪個域名。
5、If-Match
條件請求,服務器接收到附帶條件的請求后,只有判斷指定條件為真的時候,才會執行請求。
?
?
6、If-Modified_Since
這個字段會告知服務器若If-Modified-Since這個字段值早于資源的更新時間,則希望能夠處理該請求,否則返回304?NotMOdified。這個字段用于確認代理或客戶端擁有的本地資源的有效性。獲得資源的更新日期時間,可以使用Last-Modified來確定。
?
7、If-None-Match
只有當此值與ETag的值不一致的時候,才會處理這個請求,與If-Match相反。
?
?
8、If-Range
這個字段跟Range字段一起混合使用共同作用,當If-Range的值與資源的Etag或者與資源更新的日期時間相一致,那么則返回Range范圍內的內容,否則,返回全部內容。
?
?
?
9、If-Unmodified-Since
這個字段與If-Modified-Since相反,此字段告訴服務器,指定的請求資源只有在字段值內指定的日期時間之后未發生更新的情況下,才能處理請求,否則返回412的狀態碼。
?
10、Max-Forwards
用來指定中間中轉的服務器的個數
?
?
通過TRACE或者OPTIONS方法,發送包含首部字段Max-Forwards的請求的時候,該字段以十進制整數形式指定可經過的服務器的最大數目。服務器在往下一個服務器轉發請求之前,?該值減1后再重新賦值。當服務器收到Max-Forwards的值為0的請求的時候,則不再進行互贊發,而是直接返回響應。
使用 HTTP 協議通信時,請求可能會經過代理等多臺服務器。途中,
如果代理服務器由于某些原因導致請求轉發失敗,客戶端也就等不到
服務器返回的響應了。對此,我們無從可知。
?
?
11、Proxy-Authorization
這個是客戶端與代理之間的認證。當客戶端接收到從代理服務器發來的認證質詢的時候,客戶端會發送包含該字段的請求,以告知服務器認證所需的信息。
?
12、Range
指定請求部分資源的字節范圍,
Range: bytes=5001-10000
?
上面代碼的意思是請求獲取從第50001字節到10000字節之間的資源。接收帶附帶Range字段的請求的服務器,會在處理請求之后返回狀態碼為206?Partcial?Content的響應,無法處理的時候則返回狀態碼200?OK以及全部資源。
?
13、Referer
這個首部字段會告知服務器請求的原始資源的URI。
客戶端一般都會發送Referer這個字段給服務器。但當直接在瀏覽器的地址欄輸入URI的時候,處于安全性的考慮,也可以不發送該首部字段
?
?
?
14、TE
這個字段會告知服務器,客戶端能夠處理響應的傳輸編碼方式以及相對優先級。它和Accept-Encoding很像,但是用于傳輸編碼。
首部字段 TE 除指定傳輸編碼之外,還可以指定伴隨 trailer 字段的分
塊傳輸編碼的方式。應用后者時,只需把 trailers 賦值給該字段值。
TE: trailers
?
15、User-Agent
這個字段是用來傳達瀏覽器和用戶代理等信息
?
(三)、響應首部字段。
1、Accept-Ranges
用來告訴客戶端,服務器是否能夠正確處理范圍請求,以指定獲取服務器某個部分的資源。該值有兩個,可以處理范圍請求的時候指定為其byte,反之指定為none。
2、Age
這個字段能告知客戶端,源服務器在多久前創建了響應,字段值的單位為秒。
若創建該響應的服務器是緩存服務器,Age值是指緩存后的響應再次發起認證到認證完成的時間值。
?
3、ETag
這個字段是用來告知客戶端實體的標志,它是一種可將資源以字符串形式做唯一性標識的方式。服務器會為每份資源分配對應的ETag
需要注意的是,資源被緩存的時候,英文版和中文版的網站,雖然資源的路徑是一樣的,但是其在緩存服務器上對應的資源是不一樣的,也就是說,同一個URI,中文版和英文版對應的是兩個ETag不一樣的資源
?
此外ETag還有強弱之分,強的ETag是,只要資源有一點點的變化都會改變其值,而弱ETag是,只有當資源發生了根本性的變化,其值才會發生變化。
?
4、Locatoin
這個字段通常伴隨著301——303狀態碼的發生,代表資源發生重定向的時候,指向資源現在所在的位置。當客戶端收到帶有此字段的response的時候,都會強制性的嘗試對一體式的重定向資源的訪問。
?
5、Proxy-Authenticate
?
不同于request中的此字段,response的此字段是把代理服務器要求的認證消息發送給客戶端。
6、Retry-After
這個字段告訴客戶端,服務器現在正在忙,請你在指定的時間再來訪問,通常伴隨503?Service-Unvaliable響應。此字段值可以是具體的時間也可以是創建響應后的秒數
?
?
7、Vary
此字段一般是源服務器發送給代理服務器的,用來控制緩存的動作。
比如,下面這個圖中,request中指定的Accept-Lanuguage,response返回Vary指定了Accept-Langua,就是說,只有指定了該Accept-Language并且值是對的上的,才會給返回緩存。否則要從源服務器中重新獲取資源。
?
(四)、實體首部字段
1、Allow
這個字段是服務器用來通知客戶端其所能接受的方法,當服務器收到不支持的HTTP方法的時候,就會以狀態碼405?Method?Not?Allowed作為響應返回,與此同時,還會把所有能支持的HTTP方法寫進首部字段Allow后返回。
?
2、Content-Encoding
這個字段是服務器告訴客戶端對實體部分的主體部分選用的內容編碼方式,內容編碼是只在不丟失實體信息的前提下所進行的壓縮。
?
3、Content-Language
這個字段輸用來告訴客戶端,實體主體適用的語言(如中文或者英文等)
4、Content-Length
這個字段表示了實體主體部分的大小(單位是字節)
5、Content-Location
這個字段給出與報文主體部分相對應的URI
如
Content-Location: http://www.hackr.jp/index-ja.html
?
6、Content-MD5
客戶端會對接收的報文主體執行相同的MD5算法,然后與首部字段Content-MD5字段值進行比較。
這個字段值是一串有MD5算法生成的值,其目的在于檢查報文主體在傳輸過程中是否保持完整,以及確認傳輸到達。
?
7、Content-Range
只對范圍請求,返回響應是使用的首部字段,能告知客戶端作為響應返回的實體的那個部分符合范圍請求,字段值以字節為單位,表示當前發送部分及整個實體大小。
?
8、Content-Type
說明了實體豬體內對象的媒體類型和首部字段Accept一樣。
9、Expires
這個字段會將資源失效的日期告知客戶端。緩存服務器在接收到含有首部字段Expires的響應后,會以緩存來應答請求。在Expires字段值指定的時間之前,響應的副本會一直被保存。當超過指定的時間后,緩存服務器在請求發送過來時,會轉向源服務器請求資源。當服務器不希望緩存服務器對資源進行緩存的時候,最好在Expires字段內寫入與首部字段Date相同的時間值。
Expires: Wed, 04 Jul 2012 08:26:05 GMT
?
?
10、Last-Modified
這個字段指明資源最后修改的時間,
?
?
(五)、跟Cookie相關的首部字段
?
1、Set-Cookie
開始狀態管理所使用的Cookie信息,響應中的
Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31
Set-Cookie中的一些字段
?
| 屬性 | 說明 |
| NAME=VALUE | 賦予Cookie的名稱和其值 |
| expires=DATE | Cookie的有效期(若不明確指定,則默認為瀏覽器關閉前為止)。 需要注意的是,一旦Cookie被創建并被傳輸給客戶端,那么服務器端則不能顯示刪除此Cookie,只能覆蓋它 |
| path=PATH | 將服務器上的文件目錄作為Cookie的使用對象,若不指定則默認為文檔所在的文件目錄 |
| domain=域名 | 作為Cookie適用兌現的域名(若?不指定則默認為創建Cookie的服務器的域名) |
| Secure | 僅在HTTPS的情況下才會發送Cookie |
| HttpOnly | 加以限制,使Cookie不會被js腳本訪問 |
?
?
?
?
2、Cookie
服務器接收到的Cookie信息,當客戶端想獲得HTTP的狀態管理支持的時候,就會在請求中包含從服務器接收到的Cookie
?
?
Cookie: status=enable
?
六、HTTPS
1,HTTP的缺點
-
通信使用明文(不加密),內容可能會被竊聽
-
不驗證通信方的身份
-
無法驗證報文的完整性
?
?
2、為了防止被竊聽,需要將通信進行加密,需要注意的是,即使被加密了,加密后的報文仍然可以被抓包到。
3、加密方法
-
將通信加密,通過和SSL(安全套接層)(HTTPS) 或者TLS(安全層傳輸協議)的組合使用,加密HTTP的通信內容。
使用SSL簡歷安全通信線路之后,就可以在這條路上進行HTTP通信了。
?
-
將內容加密
這種情況下,客戶端需要對HTTP報文進行加密處理后再發送請求,這就需要客戶端和服務器端具備同樣的加密解密機制。因為這不是對線路濟寧加密處理,所以內容仍有被篡改的風險。
?
?
3、因為HTTP中的request和response都不會對通信方進行確認,也就是說返回來的response有可能不是我們要的情況。這是因為任何人都可以發起請求,此外服務器只要接收到請求,就會給出一個response(ip和端口沒有限定的情況下)
?
SSL提供了一種證書的手段,可以用于確定方。證書是用于證明服務器和客戶端是實際存在的,只要能夠確認客戶端或服務端持有的證書,即可判斷通信方的真實意圖。
?
?
4、HTTP無法證明報文完整性
所謂完整性就是指信息的準確度,這樣接收到的內容就有可能有誤。也就是說,發送出去的request和response,是沒有辦法證明和接收到的是一致的。
?
?
6、HTTP+加密+認證+完整性保護 =?HTTPS
?
?
?
?
7、HTTPS是身披著SSL外殼的HTTP
HTTP先跟SSL進行通信然后再跟TCP進行通信。
?
?
8、SSL的加密方法
共享密鑰加密方法
?
SSL采用的是一種叫公開密鑰加密的處理方法,也叫對稱密鑰加密,也就是加密和解密同用一個密鑰。
使用一堆非對稱的密鑰,一把叫做私有密鑰,另一把叫做公開密鑰,私有密鑰不能讓其他任何人知道,而公開密鑰則可以隨意發布,任何人都可以獲得。
使用公開密鑰加密方式,發送秘聞的一方使用對方的公開密鑰進行加密處理,對方收到被加密的信息后,再使用自己的私有密鑰進行解密。
?
?
9、HTTPS的加密機制。
HTTPS采用共享密鑰和公開密鑰兩者混合加密機制。
?
?
?
10、證明公開密鑰本身是正確的公開密鑰的方法:使用由數字證書認證機構和其相關機關頒發的公開密鑰證書。
?
?
?
?
11、HTTPS的安全通信機制
?
?
-
第一步,客戶端通過發送ClientHello報文開始SSL通信。報文中包含客戶端支持的SSL的指定版本、加密組件列表(所使用的的加密算法及密鑰長度)
-
服務器可進行SSL通信的時候,會以Server?Hello報文作為應答。和客戶端一樣,在報文中包含SSL版本以及加密組件。服務器的加密組件內容是從接受到的客戶端加密組件內篩選出來的。
-
之后服務器發送Certificate報文,報文中包含公開密鑰證書
-
最后服務器發送Server?Hello?Done?報文通知客戶端,最初的SSL握手結束
-
SSL第一次握手結束之后,客戶端以Client?Key?Exchange報文作為回應。報文中包含通信加密中使用的Pre-master隨機密碼串,該報文已經使用第三部中的公開密鑰進行加密
-
接著客戶端繼續發送ChangeClipher?Spec報文,該報文會提示服務器,在此報文之后的通信會采用剛剛Pre-master密鑰加密。
-
客戶端發送Finished報文。該報文包含鏈接至今全部報文的整體校驗值。這次握手協商是否能夠成功,要以服務器是否能夠正確解密該報文作為判定標準
-
服務器同樣發送ChangeClipher?Spec報文
-
服務器同樣發送Finished報文
-
服務器和客戶端的Finished報文交換完畢之后,SSL連接就算建立完成。之后的通信都會受到SSL的保護,開始發送HTTP請求。
-
應用層協議通信,HTTP響應
-
最后有客戶端斷開連接。斷開連接時,發送close_notify報文。
-
TCP四次揮手。
?
?
?
七、認證機制
1、為了核對對方的身份,需要用到以下的信息
-
密碼
-
動態令牌(僅限本人持有的設備內顯示的一次性密碼)
-
數字證書(僅限設備持有的信息)
-
生物認證
-
IC卡等
2、HTTP使用的認證方式
-
BASIC認證
-
DIGEST認證
-
SSL客戶端認證
-
FormBase認證
?
2.1?BASIC?認證(不常用)
?
?
-
當請求的資源需要BASIC認證的手,服務器會隨狀態碼401Authorization?Required,返回帶WWW-Authenticate首部字段的響應。該字段內包含認證的方式計一安全域字符串
-
接收到狀態碼401的客戶端為了通過BASIC認證,需要將用戶ID以及密碼發送給服務器。發送的字符串內容是由用戶ID和密碼構成兩者中間以冒號連接后,再經過Base64編碼處理
比如,用戶ID為lwl,密碼是lwl,連接起來就會形成lwl:lwl這樣的字符串。然后經過Base64編碼,最后的字符串是bHdsOmx3bA==。把這個字符串寫入首部字段Authorization之后,發送請求。(瀏覽器會自動編碼并發送請求)
-
接收到包含首部字段Authorization請求的服務器,會對認證信息的正確性進行驗證。如驗證通過,則返回一條包含Request-URI字段的響應。
?
2.2?DIGEST認證
DIGEST認證同樣適用質詢/響應的方式,但不會像BASIC認證那樣直接發送明文密碼。
所謂質詢方式是指,一開始一方會先發送認證要求給另一方,接著適用從另一方發送過來的質詢碼計算生成響應碼,最后將響應碼返回給對方進行認證的方式
?
DIGEST認證認證的步驟
?
-
請求需要認證的資源時,服務器會隨著狀態碼為401,返回帶WWW-Authenticate首部字段的響應,該字段內包含之響應方式認證所需的臨時質詢碼(隨機數)。首部字段 WWW-Authenticate 內必須包含 realm 和 nonce 這兩個字段的信息。客戶端就是依靠向服務器回送這兩個值進行認證的。nonce 是一種每次隨返回的 401 響應生成的任意隨機字符串。該字符串通常推薦由 Base64 編碼的十六進制數的組成形式,但實際內容依賴服務器的具體實現。
-
接收到401狀態碼的客戶端,再次發送的request中包含DIGEST認證必須的首部字段Authorization信息。
-
再次接收到請求的服務器,會確認認證信息的增缺陷。認證通過后則返回包含資源的響應。
?
2.3?SSL客戶端認證。
?
-
接收到需要認證資源的請求,服務器會放Certificate?Request報文,要求客戶端提供客戶端證書。
-
用戶選擇將發送的客戶端證書后,客戶端會把客戶端證書信息以Client?Certificate報文方式發送給服務器
?
-
服務器驗證客戶端證書驗證通過后方可領取證書內客戶端的公開密鑰,然后開始HTTPS加密通信。
?
SSL 客戶端認證不會僅依靠證書完成認證,一般會和
基于表單認證(稍后講解)組合形成一種雙因素認證(Two-factor
authentication)來使用。所謂雙因素認證就是指,認證過程中不僅需要密碼這一個因素,還需要申請認證者提供其他持有信息,從而作為另一個因素,與其組合使用的認證方式。
換言之,第一個認證因素的 SSL 客戶端證書用來認證客戶端計算機,另一個認證因素的密碼則用來確定這是用戶本人的行為。
通過雙因素認證后,就可以確認是用戶本人正在使用匹配正確的計算機訪問服務器
?
2.4、基于表單認證(常用)
基于表單認證本身是?通過服務器daunt的Web應用,將客戶端發送過來的用戶ID和密碼與之前登陸過的信息做匹配進行認證的。因為HTTP無狀態,因此使用Cookie來管理Session。
?
-
客戶端將用戶ID和密碼等登陸信息放入報文的實體部分,通常是以POST方法把請求發送給服務器。而這時,會使用HTTPS通信來進行HTML表單畫面的顯示和用戶輸入數據的發送。
-
服務器會發送用以識別用戶的Session?ID。通過驗證從客戶端發送過來的登錄信息進行身份驗證,然后把用戶的認證狀態與Session?ID綁定后記錄在服務器端。向客戶端返回響應時,會在首部字段 Set-Cookie 內寫入 SessionID(如 PHPSESSID=028a8c…)Session ID 應使用難以推測的字符串,且服務器端也需要進行有效期的管理,保證其安全性。另外,為減輕跨站腳本攻擊(XSS)造成的損失,建議事先在Cookie內加上 httponly 屬性。
-
客戶端接收到從服務器端發來的 Session ID 后,會將其作為Cookie 保存在本地。下次向服務器發送請求時,瀏覽器會自動發送Cookie,所以 Session ID 也隨之發送到服務器。服務器端可通過驗證接收到的 Session ID 識別用戶和其認證狀態
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的HTTP详解(更新完结)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 齐兴皓 团队项目(任务五):项目回顾
- 下一篇: Pandas熊猫框架