首先 Http 是互聯網上的一個協議css
超文本傳輸協議(英語:HyperText Transfer Protocol,縮寫:HTTP)是一種用於分佈式、協做式和超媒體信息系統的應用層協議[1]。HTTP是萬維網的數據通訊的基礎。 設計HTTP最初的目的是爲了提供一種發佈和接收HTML頁面的方法。經過HTTP或者HTTPS協議請求的資源由統一資源標識符(Uniform Resource Identifiers,URI)來標識。java
前面的引用裏面講 HTTP 或者 HTTPS 協議請求的資源由統一資源標識符(Uniform Resource Identifiers,URI)來標識,其實除了 URI ,咱們常常見到的還有 URL,這二者有什麼區別呢? 看下定義:android
URI 是用來標示 一個具體的資源的,咱們能夠經過 URI 知道一個資源是什麼。 URL 則是用來定位具體的資源的,標示了一個具體的資源。bash
他們兩個是包含關係的,URL 屬於 URI ,關係以下:服務器
參考連接markdown
URI 和 URL 都要遵循的格式以下網絡
scheme:[//authority][/path][?query][#fragment]
複製代碼
摘自維基百科: tcp
URL 的語法格式:(帶[] 爲可選項)分佈式
protocol :// hostname[:port] / path / [?query]#fragment
複製代碼
指定使用的傳輸協議,最常常見到的是 HTTP 協議,其餘的協議還有 https、file、ftp等學習
指的是存放資源的域名或者是 IP 地址,有時主機名前也能夠包含鏈接到服務器的用戶名和密碼,格式(username:password@hostname)
端口號是可選的,若是不填寫的話,http 訪問默認使用 80 端口,https 默認使用 443 端口
路徑表明的是一個服務器上目錄或者是具體文件的地址,由零個或者多個 / 隔開。
query 表明是在當前路徑下的查找的限定符,前面是由 ?開頭,多個參數的時候用 & 符號隔開,採用 key-value 的形式,key 和 value 用= 隔開,格式爲 ?key = value & key1 = value1 。
表明的是資源中的某一個片斷。能夠指定 fragment 直接定位到具體的位置
平時的 Http 請求中,通常使用的是 URL 來進行資源訪問的,好比咱們常見的 GET 請求, Android 官方文檔的 GET 請求爲例:
https://developer.android.com/about/versions/pie#androidnbsp9
// https 的默認端口是 443 端口,若是是 http 請求則是默認 80 端口
https://developer.android.com:443/about/versions/pie#androidnbsp9
複製代碼
類型 | 對應 |
---|---|
protocol | https |
hostname | developer.android.com |
port | 443 |
path | about/versions/pie |
query | 這裏未體現 |
fragment | androidnbsp9 |
具體的能夠去維基百科看看
常常據說的網絡模型有 OSI七層網絡模型 和 TCP/IP四層網絡模型兩種,分別以下:
TCP/IP 模型是借鑑了OSI 模型的一些概念而創建起來的,OSI 模型實際上是一種理論下的模型,而 TCP/IP 協議如今已經成爲互聯網的標準。
TCP/IP 協議從字面意思來看是,並不單純的指傳輸層的 TCP 協議和網路層的 IP 協議,其實指的是進行 IP 通訊過程當中所用到的協議簇的統稱,只是由於 TCP 協議和 IP 協議是 TCP/IP 協議中很重要的協議,因此就稱之爲 TCP/IP 協議了。
網絡層的 IP/ICMP 協議、傳輸層的 TCP/UDP 協議、傳輸層的 HTTP 協議等都屬於 TCP/IP 協議,這些協議與 TCP/IP 緊密聯繫,互相協做,一塊兒完成完整的 HTTP 通訊,也稱爲 TCP/IP 網際協議羣。
互聯網進行通訊時,須要相應的網絡協議,TCP/IP 本來就是爲使用互聯網而開發制定的協議族。所以,互聯網的協議就是 TCP/IP,TCP/IP 就是互聯網的協議。
先看下 TCP/IP 協議的網絡數據傳輸圖:
客戶端要發送的數據,須要通過層層協議進行包裝,而後經過電信號進行傳輸,而後服務端在通過協議進行層層解開包裝獲取客戶端傳入的數據進行處理。
這種分層的好處是很大的:
- 各層之間是獨立的。某一層並不須要知道它的下一層是如何實現的,而僅僅須要知道該層經過層間的接口所提供的服務。這樣,整個問題的複雜程度就降低了。也就是說上一層的工做如何進行並不影響下一層的工做,這樣咱們在進行每一層的工做設計時只要保證接口不變能夠隨意調整層內的工做方式。
- 靈活性好。當任何一層發生變化時,只要層間接口關係保持不變,則在這層以上或如下各層均不受影響。當某一層出現技術革新或者某一層在工做中出現問題時不會連累到其餘層的工做,排除問題時也只須要考慮這一層單獨的問題便可。
- 結構上可分割開。各層均可以採用最合適的技術來實現。技術的發展每每是不對稱的,層次化的劃分有效避免了木桶效應,不會由於某一方面技術的不完善而影響總體的工做效率。
- 易於實現和維護。這種結構使得實現和調試一個龐大又複雜的系統變得易於處理,由於整個的系統已被分解爲若干個相對獨立的子系統。進行調試和維護時,能夠對每一層進行單獨的調試,避免了出現找不到問題、解決錯問題的狀況。
- 能促進標準化工做。由於每一層的功能及其所提供的服務都已有了精確的說明。標準化的好處就是能夠隨意替換其中的某幾層,對於使用和科研來講十分方便。
應用層做爲 TCP/IP 協議的最高層,直接面向用戶,是咱們平時開發過程當中遇到的最多的。可是網絡中傳輸的數據是字節流,不可以被程序很好的識別,因此須要在應用層定義協議,規範數據格式,供用戶去訪問,常見的應用層的協議有:HTTP、FTP、SMTP 等。咱們最常用的就是 HTTP 和 HTTPS 協議了,HTTPS 協議是在 HTTP 協議的基礎上對數據進行了加密處理,下面章節再講 HTTP 和 HTTPS 協議。
傳輸層顧名思義就是負責數據的傳輸的,在數據通過這一層的時候,會添加傳輸層的協議頭,而後交給下一層網絡層去處理,常見的傳輸層的協議有 TCP 協議、UDP 協議,對好比下:
TCP 是須要先創建鏈接之後才能夠開始傳輸的,在傳輸過程當中是用流量控制、擁塞控制等保證數據的正確傳輸。TCP 爲了保證可靠傳輸,給每一個包一個序號,同時須要也保證了傳送到接收端實體包的有序接收,而後接收端實體對已經成功接收到的字節發回一個相應的確認(ACK),若是發送端實體在合理的時間內未收到回覆 ACK,那麼就會認爲數據丟失,而後從新傳送數據。 UDP 則是不須要創建鏈接的,沒有使用流量控制、擁塞控制,只管本身發送數據,不能保證數據可以被正確的接收
創建一次 TCP 鏈接,須要進行三次握手,握手以後纔開始數據傳輸,斷開鏈接的時候須要經歷四次揮手。 UDP 則不須要這些過程,直接發送數據。
TCP面向字節流,其實是TCP把數據當作一連串無結構的字節流; UDP是面向報文的。
TCP 只要創建鏈接之後才能傳輸數據,只能鏈接兩個端點,採用端對端方式傳播 UDP 則能夠鏈接多個端點,支持1對1,1對多,多對1,多對多通訊
當網絡擁塞的時候, TCP 能減小向網絡中注入數據速率和數量,緩解網絡擁塞 UDP 則不會進行控制,一直髮送數據。
上面圖中的數據,在通過網絡層的時候,會加上 IP 首部,IP 首部裏面就包含了源 IP 地址和目標 IP 地址,網絡層就是負責將數據從源 IP 地址 傳輸到目標 IP 地址,是點對點的通訊。
IP 首部以下:
網絡層設計的東西仍是比較多的,這裏暫時不寫了,之後有空再填坑。
鏈路層負責將0、1序列劃分爲數據幀從一個節點傳輸到臨近的另外一個節點,這些節點是經過MAC來惟一標識的(MAC,物理地址,一個主機會有一個MAC地址)。
前面講了分層模型,這裏講下 HTTP 的報文格式:
HTTP 報文又分爲兩部分:
請求報文由 請求行(request line)、請求頭部(header)、空行和請求體4個部分組成
來看一個具體的請求報文:
請求行分爲三個部分:請求方法、請求地址和協議版本
請求頭部爲請求報文添加了一些附加信息,由「key/value」對組成,每行一對,key和value之間使用冒號分隔;
常見的請求頭:
請求數據也就是咱們常說的請求體,包含了咱們發送給服務器的數據,若是是 GET 請求,path 後面的內容會出如今這裏,若是是 POST 請求,請求體題 BODY 裏面的內容會出如今這裏
一個實際的請求實例
響應報文由狀態行、響應頭部、空行以及響應數據四部分組成
由3部分組成,分別爲:協議版本,狀態碼,狀態碼描述 協議版本就是 HTTP 的協議版本, 狀態碼是定義的 code ,規定了服務器返回的狀態,狀態碼描述是對狀態碼的解讀。常見的狀態碼和狀態碼描述以下:
這個與請求頭部相似,爲響應的報文添加了一些信息。
用於存放須要返回給客戶端的數據信息,一般咱們開發中請求接口最關心的就是這裏的數據。
一個實際的請求實例
TCP 雖然是面向字節流的,可是傳送的數據單元是報文段,一個 TCP 報文段分爲首部和數據兩部分,TCP 的所有功能體如今各首部中的個字段的做用。
TCP 報文段的首部的 20 個字節是固定的,後面的 4n(每行四個字節,n 爲整數)是根據須要而增長的選項,因此最小狀況是沒數據,只有 20 個字節。
TCP 報文頭部除了包含源端口和目標端口外,還定義了一些控制位,
控制位 | 含義 |
---|---|
URG: | 緊急數據(urgent data)—這是一條緊急信息 |
ACK: | 確認已收到 |
PSH: | 當兩個應用進程交互時,一段鍵入一個命令就能當即獲得對方的響應 |
RST: | 當 RST =1 時,代表 TCP 休閒嚴重差錯,須要從新創建鏈接 |
SYN: | 當連接創建時用來同步序號 |
FIN: | 用來釋放一個鏈接 |
重點看下和 TCP 三次握手和四次揮手相關的控制位:
三次握手指的是 TCP 鏈接創建的過程,握手須要在客戶端和服務端交換三個 TCP 報文段,三次握手過程以下圖所示:
圖中的 A 是客戶端程序,B 是服務端程序,最初的時候鏈接未創建,兩端的 TCP 進程都處於 CLOSED(關閉) 狀態,而後進行了 A 主動打開鏈接,B 被動打開鏈接。
A 建立本地的傳輸控制模塊 TCB,而後建立一個 TCP 報文段,報文段中:
SYN = 1 同步控制位(=1 時候不能攜帶數據)
seq = x;序號,供 B 確認
複製代碼
因爲此時 SYN 爲 1,不能給攜帶數據,可是要消耗掉一個序號 seq,發送完之後客戶端進入 SYN-SENT(同步已發送) 狀態
B 在接收到 A 傳來的創建鏈接請求之後,若是是贊成創建鏈接,則會給 A 響應以確認本身贊成創建鏈接,報文段以下:
SYN = 1 同步控制位(=1 時候不能攜帶數據)
ACK = 1 確認控制位
ack = x + 1 在 A 傳來的序號 x 基礎上+1,以確認
seq = y 建立本身的 序號 y,供 A 去確認
複製代碼
這一步主要是添加了 ACK =1 ,表示確認有效,而後再把 A 傳來的 x 值加一,以供 A 去確認,再建立一個本身的序號 y。最終把 TCP 報文傳遞給 A,B 進入 SYN-RCVD(同步收到) 狀態。
ps:這一步能夠把這一個報文段分爲兩個去發送,一個 ACK=1,ack = x+1;一個是 SYN=1,seq = y,這樣的話,就變成了四次握手,不過最終效果是同樣的。白白增長了一步操做。
A 在接收到 B 傳來的 TCP 報文之後,還須要再次向 B 確認,
ACK = 1
ack = y + 1
seq = x + 1
複製代碼
此時把 ACK = 1表示確認,而後把 B 傳來的 seq +1 做爲確認序號 ack ,消耗本身的序號數 seq = x+1,這個時候沒有 SYN 控制位,此 TCP 是能夠攜帶數據,可是會消耗本身的序號,若是不攜帶數據的話,不會消耗本身的序號,等到下次數據傳輸的時候seq 仍是 x+1,這個時候 TCP 鏈接已經創建,A進入ESTAB-LISHED(已創建鏈接)狀態。
當 B 收到 A 傳來的 TCP 報文的時候,也進入ESTAB-LISHED(已創建鏈接)狀態。
至此三次握手就完成了。
爲何進行三次握手而不是兩次握手,第三次 A 還要去給 B 去確認呢?
這主要是爲了防止已經失效的連接請求報文忽然又傳送到了 B,而且 B 也給了返回,可是這個時候 A 是不須要這個返回的,因此不會對 B 的返回進行響應,可是 B 還在一直等待 A 的返回,這就致使了 B 的資源白白浪費了。
若是有了第三步握手,B 就認爲沒有和 A 進行創建鏈接,就不會對 A 的請求作出響應。
四次揮手指的是 TCP 鏈接斷開的過程,揮手須要在客戶端和服務端交換四個 TCP 報文段,四次揮手過程以下圖所示:
剛開始的時候 A 和 B 是已經創建了 TCP 鏈接的,處於 是能夠正常通訊的,再不須要該鏈接的時候執行了上圖中的過程,下面依次分析
當客戶端 A 沒有數據要和服務器 B 交互的時候,就會發送一個 TCP 報文,
FIN = 1 標誌着要斷開此 TCP 鏈接
seq = u ;u 等於前面已經傳送過數據的最後一個字節的序號 +1
複製代碼
A 發送了這個 TCP 報文之後就進入了 FIN-WAIT1(終止等待 1)狀態,等待 B 的確認,這裏要注意的是:即便 FIN 報文段不攜帶數據,也會將序號消耗掉一個。
B 接收到 A 發來的請求終止 TCP 鏈接的報文之後,要回復給 A 一個確認報文,
ACK = 1 ;ACK =1 表示確認
ack = u+1 ;把 A 傳來的 u 加 1 之後,經過確認序號返回給 A
seq = v ;自身前面已經傳送過的數據的最後一個字節的須要+1 之後做爲本次序號傳遞給 A
複製代碼
B 發送完了本次確認報文之後就進入了 CLOSE-WAIT(關閉等待)狀態,這時的 TCP 鏈接處於半關閉狀態,即:A 沒有數據要發送給 B,可是 B 仍是有可能發送數據給 A 的,這個時候 A 仍然會對 B 發送的數據進行接收。
A 收到 B 傳來的確認報文之後,就進入了 FIN-WAIT-2(終止等待 2) 狀態,等待 B 發送最後的鏈接關閉報文
這個時候發生在 B 也沒有數據要發送給 A 了,這時 B 要發送報文告訴 A,B 自身也要關閉鏈接。
FIN = 1; 關閉鏈接控制位
ACK = 1; 確認控制位
seq = w; 若是在第二次握手之後 B 沒有發送數據,那麼 w = v,若是發生了數據發送,就 w > v
ack = u+1;重複發送上次發送的 u+1確認序號。
複製代碼
此時 B 發送了之後自身進入了LAST-ACK(最後確認)狀態,等待 A 的確認
A 在收到 B 的請求關閉鏈接的報文之後,在確認報文段中將 ACk 置爲 1,表示確認,並給 B 回覆:
ACK = 1;確認控制位
ack = w+1;確認序號爲 B 發來的 w+1
seq = u+1;自身的序號+1
複製代碼
A 在發送了最後的報文之後,自身就進入了TIME-WAIT(時間等待)狀態,此時的 TCP 鏈接尚未釋放掉,必須通過時間等待計時器(TIME-WAIT timer)設置的時間 2MSL 之後 A 纔會進入到 CLOSED 狀態。
只要 B 接收到 A 發來的確認報文,就進入了 CLOSED 狀態,可是因爲 A 要再發送報文之後等待一段時間纔會關閉,因此B 的關閉會比 A 早一點。
TCP協議是一種面向鏈接的、可靠的、基於字節流的運輸層通訊協議。TCP是全雙工模式,這就意味着,當主機1發出FIN報文段時,只是表示主機1已經沒有數據要發送了,主機1告訴主機2,它的數據已經所有發送完畢了;可是,這個時候主機1仍是能夠接受來自主機2的數據;當主機2返回ACK報文段時,表示它已經知道主機1沒有數據發送了,可是主機2仍是能夠發送數據到主機1的;當主機2也發送了FIN報文段時,這個時候就表示主機2也沒有數據要發送了,就會告訴主機1,我也沒有數據要發送了,以後彼此就會愉快的中斷此次TCP鏈接。
還有是爲了在確保客戶端和服務端雙方不須要通訊的時候,及時的斷開鏈接,不在佔用資源服務器資源。
UDP 的報文結構就比 TCP 報文結構簡單了不少。
上面的報文完整的展現了一次完整的 http 請求響應的過程。
前面分析過這裏再也不描述
第一步:客戶端發送 HTTP 請求報文給服務端, 第二步:服務端收到客戶端發送來的 HTTP 請求報文之後經過 TCP 報文告訴客戶端已經收到
這段其實本身也不是很清楚,有興趣的能夠看下這裏
第一步:服務端發送 HTTP 響應報文給客戶端, 第二步:客戶端收到服務端發送來的 HTTP 響應報文之後經過 TCP 報文告訴服務端已經收到
前面分析過這裏再也不描述
以上內容都是本身在學習 HTTP 網絡知識的時候總結的,有些地方可能不是很準確,歡迎一塊兒交流溝通。
事實上如今使用 http 協議的已經不多見了,大多數都已經實現了 https 協議,有興趣的看下這篇 計算機網絡學習之 Https 相關。
歡迎關注個人公衆號: