PS:
簡書的網址真不是給人看的。。。我單獨開了一個網址可以重定向到我的簡書主頁。
博客地址:flutterall.com
WHY
Http協議作為網絡編程的核心是重中之重。網絡上關于Http協議的在Android中的應用有很多,但是很多僅僅講到了一些Http報文。這當然是一個知識點,但是如果沒有全局的了解,僅僅熟悉Http報文很大程度上是一知半解,真的是書到用時方恨少。這篇文章呢能,是我對Http知識在Android應用中的必須了解的一些總結,在這里與大家分享一下。
WHAT
看完這篇文章你能了解到什么?
- URL的構成
- Http報文
- Http報文的組成
- Http報文的元素解釋
- Http請求方法簡述
- 一個Http報文的傳輸流程
- 如何建立數據傳輸通道
- 發送請求與接收響應的流程
- 周邊知識拓展
本篇文章可用一句話總結:Http數據的發送與接收。分為三大部分講解。
- 發送的數據源
- 如何發送
- 如何解析收到的數據
HOW
URL
生活中得任何一種東西都有一個它的地址。無論是房屋編號、郵箱編號、手機號碼、身份證號碼等。根據這個編號我們就能拿到這個東西所表示的東西。比如北大地址是:北京市海淀區頤和園路5號。那么我開車到北京市的海淀區的頤和園路的5號就到了北大。
同樣,Web中的資源也有它所屬的地址。這就是:Uniform Resoure Locator:統一資源定位器,用來標識某個web資源(可以是音頻、視頻、文本等)在web服務器上的位置。通過這個URL我們可以知道:1.這個網絡資源的具體位置。2.訪問這個網絡資源的方式。
- 開車-->訪問方式
- 北大地址-->具體位置
URL構成分析
我們在瀏覽器程序中輸入http://www.baidu.com/img/logo.gif 即可看到百度的Logo。這一個小小的請求即包含了一個完整的HTTP生態鏈。這里引用外國的一個圖文來看看一個URL是如果構成的:
下面我們一起逐個攻破:
Protocol
也可以用Scheme來表示其意義。它表示解析URL的程序以什么形式去解析URL。比如:Http(超文本傳輸協議),需要以http開頭,不區分大小寫。緊接著分號,然后是URL的其他部分。故HTTP協議總體來看長這個樣子http://host/index.html。 常見的協議很多,我這里簡單列舉幾個:
協議名稱 | 簡介 |
---|---|
HTTP | 超文本傳輸協議,大概格式是這樣:http://<host>:<port>/<path>?<query>#<flagment>。 |
HTTPS | 可以認為是HTTP+SSL。用來進行加密網絡數據的傳輸 |
FTP | 文件傳輸協議。詳情在百度上Google一下 |
SMTP | 簡單郵件傳輸協議。詳情在百度上Google一下 |
Domain Name
就是存放web資源的老窩。注意!這里講的是老窩是家,不是家里的某個具體位置。這個東西用來標志WEB資源所在的服務器的地址。我們要訪問的web資源都有屬于他的IP地址,通過這個IP地址建立對應的TCP連接進而進行訪問資源。但是,IP地址不易記憶。故,為了方便W資源的訪問,我們用一個我們可以方便記憶的地址來代表著這個IP地址,在進行Web資源的訪問時,將這個用來記憶的地址轉換成對應的IP地址后再進行訪問對應的WEB資源。例如,百度主頁的IP地址是119.75.217.109,進行Http訪問時,需要在瀏覽器中輸入:http://119.75.217.109/ 即可訪問百度主頁。但是像119這樣的東東,很難記得。所以我們可以用http://www.baidu.com 來表示百度主頁的IP地址。當訪問百度主頁的時候,通過域名的HTTP請求會通過DNS將www.baidu.com解析成119.75.217.109,最終對該IP地址上資源的操作。
這就是DNS【Domain Name System】的來源,域名與IP地址是N:1的關系。我們可以在Windows中的命令提示符中通過ping www.baidu.com 得到百度的IP地址。所以當DNS掛了,通過http://www.baidu.com就可能訪問不了百度主頁了,但是通過IP地址任然是可以訪問百度主頁的。
獲取域名的IP地址
Port
對于HTTP請求而言,其默認端口號是80。代表著,Web服務器正在監視著80端口。
Http的底層實現不一定使用TCP協議,也可以使用其他協議。對于Http底層使用TCP協議的服務器而言,默認監視著80端口。在我的上篇博客Android網絡編程之--Socket編程中,提到建立C/S的連接需要IP地址+端口號。IP地址就是上面Domain Name提到的IP地址,端口號就是指Socket連接監聽的端口號。
Path
上面提過Domain Name是WEB資源的的家,那么Path就可以表示這個WEB資源在WEB服務器中的具體位置。例如:https://www.baidu.com/img/logo.gif 中百度的logo的Path就是/img/logo.gif
Query
在訪問網絡資源時,有時候我們可能不太確信訪問的資源是否存在,我們可以通過查詢WEB資源的方式去訪問網絡資源。例如,我要訪問百度貼吧的“Android吧”:http://tieba.baidu.com/f?&kw=android ,“?”及其后面是Http的查詢組件。以查詢的方式訪問相應的WEB資源。
嘗試了一下,將上面鏈接的Android替換成IOS即可訪問IOS吧。http://tieba.baidu.com/f?&kw=ios
依次類推,替換成xiaomi即可訪問小米吧........
Parameters
在上面的訪問Android吧的栗子中,kw=android就是該Http的參數。通過這個參數可以縮小要訪問的web資源的范圍。
通常的HTTP查詢參數的例子是:key=value作為一個參數,使用“&”符號將每一對參數依次分開拼接在一起。例如:
http://tieba.baidu.com/f?kw=android&fr=ala0&tpl=5
Fragment
代表網頁中的一個位置。其右面的字符,就是該位置的標識符。比如,http://www.example.com/index.html#print 就代表網頁index.html的print位置。瀏覽器讀取這個URL后,會自動將print位置滾動至可視區域。
當發起一個http://www.example.com/index.html#print 請求時,print參數是不會發送給服務器的。瀏覽器發送的是http://www.example.com/index.html 請求,服務器會對http://www.example.com/index.html 進行相應的處理返回,然后瀏覽器會從print的地方開始顯示HTML。
URL小結
- 構成
http://host/path - 意義
- 表明了Web資源在網絡中得位置
- 擺明了訪問該WEB資源的方式。
- DNS
域名的存在方便人們訪問WEB資源,域名通過DNS可以轉換成對應的IP地址進而進行國事訪問。
報文
在上一節的URL的講解中我們知道了一個Web資源到底是如何存在于服務器中得。這個URL就是一條高速公路,我們知道從北京到上海的路線,那么具體如何把北京的貨物運輸到上海呢?這就涉及到HTTP報文。
Http報文類似于一個貨車??,在這個??中存儲著我們要發往上海的貨物,以及對該貨物的具體描述。
本節重點在兩個地方:
-
貨物??-->HTTP報文
- 報文的構成
- 常見報文舉例
-
貨車??-->TCP連接通道建立以及傳輸數據
- 從URL到TCP連接建立
報文的組成
Http報文從整體來看分為請求報文和相應報文。下圖,是我抓取的某個網絡請求以圖文的形式展示出來。
無論是請求報文還是響應報文,其輪廓差不多。主要有三部分組成:start Line;Headers;Body.
進一步根據請求與響應進行細分,如下:
由上面的圖示,引出Http請求報文和響應報文標準格式是:
請求報文
<Request Method>空格<Request URL>空格<Http版本號>
<Key>:<Value>
<Key>:<Value>
不可缺少的空行
<Entity Body>響應報文
<Http Version>空格<Status Code>空格<Reason Phrase>
<Key>:<Value>
<Key>:<Value>
不可缺少的空行
<Entity Body>
在 ** 請求報文和相應報文的格式中,需要注意幾點: **
- 每一行的結束均以CRLF(回車+換行)表示該行數據已經結束。
- 對于Header(包含請求的請求正文、響應的消息報頭)來說,均以鍵值對的形式表示該行header的意義,以CRLF表示該行Header的結束。
- 對于某些請求而言可以沒有請求數據(請求數據可以稱之為請求體又有人稱之為請求正文)。
- 起始行和Header之間沒有空行,但是Header與Body之間一定有空行作為區分。
OK,下面開始我們就依照上面的圖示,一個一個講解報文的每一部分。這是一條遙遠的天路,不過我們乘坐的是火箭??。沒什么理解上的難度,多看兩遍就理解了。
這里我們先對HTTP報文的每一部分進行一個簡要介紹,先理解大致輪廓,后面我們再逐個細看。
概論請求報文:
請求行
<Request Method>空格<Request URL>空格<Http版本號>
比如:POST /errconf HTTP/1.1
- Request Method(請求方法)
請求方法代表著發起Http請求的源端希望對服務端進行的相應操作。常用的請求方法有:POST、GET。
在這里POST代表著客戶端發起的是一個POST請求方法,這個POST請求方法代表著客戶端要上傳數據到服務端。 - Request URL
上面一小節中我們講過URL,是Web資源的地址。
在這個例子中/errconf 就是我們要請求的Web資源地址,可以不是 - Http版本號
報文所使用的Http版本號,格式為HTTP/x.y?!皒.y”就跟我們app的5.1版本和5.2版本一個意思。
請求頭
請求頭由許多的鍵值對組成,每個鍵值對以CRLF(空格換行符)進行隔開,每一個鍵值對長這樣:<Key>:<Value>
Key有很多種,有Http目前提供的現成的,例如上面例子中的:
- Content-Type
- Content-Length
- User-Agent
- Host
請求頭的具體的每一個現成的key的意義在后面會講的。
請求數據
又稱之為:請求體、請求正文。對于目前的GET方法沒有請求數據、POST請求而言需要請求數據。其他的有沒有請求數據,后面講解。
概論響應報文:
狀態行
用來返回服務端對于客戶端的請求結果,也就是對于客戶端的請求,服務端發生了上面。格式為:
<Http Version>空格<Status Code>空格<Reason Phrase>
如:
HTTP/1.1 200 OK
- Http Version
表示服務器使用的Http版本 - Status Code(狀態碼)
用來給機器看各種客戶端比較統一的一個數字,不同的數字表示的意義不同。常見的狀態碼及其大概意思如下:- 200:正常
- 302/307:重定向
- 304:服務器的資源沒有被修改
- 404:請求的資源不存在
- 500:服務器報錯了
又稱之為響應碼
- Reason Phrase(原因短語)
對狀態碼的描述
例如上面??例子中"OK"就是一個原因短語,這是給人看的具體該狀態碼表示的意思。
消息報頭
格式與意義與請求報文一一致,已經提供的Header有的只能用在響應報文中,有的只能用在請求報文中,有的兩者皆可用。
響應正文
又稱之為響應體,就是Http請求想要的東西,可以是文本、音頻、視頻等等。
OK!現在我們對Http的報文有了大致了解,下面開始每一寸方的解釋了。
Request Method(請求方法)
Http的請求方法代表了客戶端想對服務器進行的操作,比如:POST、GET、HEAD、PUT、DELETE、TRACE、OPTIONS。
常用的不過于CRUD四個。增:PUT;刪:DELETE;改: POST;查: GET。
GET
這個方法可謂是在熟悉不過了,用于向服務器請求數據。
下面以訪問 http://apicloud.mob.com/wx/article/category/query?key=1c66066891045 為例子
GET請求沒有請求體。對于GET請求的請求參數在URL后面加上一個“?”的后面,參數以key=value的形式。參數與參數之間使用“&”進行連接。
由于GET請求是通過URL傳輸參數的,所以GET請求可以傳輸的參數是有限的。關于GET請求的詳細,請移步Google。
POST
POST請求也是我們開發中最常用到的,用于向表單提交數據使用的。該請求要傳送的數據是放在請求體中的。
在上面講解報文組成那個例子中就是一個POST請求例子,下面再拿某APP上的POST請求的例子:
在POST請求中,POST請求的請求參數放在請求體中.服務器根據POST請求體中的參數創建一個對應的頁面,然后返回給服務器。
上面的POST請求后創建的頁面如下,可能需要翻墻查看結果:
GET請求和POST請求已經能夠進行基本的增刪改查了。就像上面的POST請求就完成了一個類似PUT的動作。但是其他的幾個請求也要有所了解。
HEAD
HEAD請求跟GET請求類似,同樣沒有請求體。與GET請求不一致的是服務器返回時只返回響應頭,不返回響應體【言外之意是HEAD請求的響應頭與GET一致,只是沒有響應體而已】。
HEAD請求常用于:
- 檢查請求的URL是否有效(可以通過響應碼進行判斷)
- 根據響應頭判斷資源是否被篡改了
PUT
POST請求用于向服務器發送數據,而PUT請求用于向服務器的資源中存儲數據。對于POST請求而言,服務器會使用POST的請求體的數據創建一個由所請求的URL命名的新文件。除非特殊說明,否則POST請求的請求體只用于創建或修改該資源上。如果請求的URL在服務器中不存在,則根據該請求的主體部分創建一個由該請求URL命名的新文檔;如果該URL在服務器中已經存在,則用該主體替代他。一個PUT請求示例:
DELETE
顧名思義,這是用來刪除服務上的文件。
TRACE
trace英文指:追蹤、痕跡的意思。在HTTP請求中使用這個方法用來查看一個請求在經過網關、代理等等到達服務器后查看這個請求最終變成了什么樣。
對于這種請求而言,客戶端發送一個請求后,服務端在收到請求后會返回給客戶端一個響應,該響應正文就是服務器收到的來自客戶端的請求報文。
如上圖示例:
- 服務器發送了一個TRACE請求,該請求經過代理服務器后在請求頭中新增了一個Via首部。服務器最終收到的報文是添加了一個Via首部的報文。
- 由于是TARCE請求,服務器的處理方式是將收到的請求報文內容原樣放入響應正文中返回給客戶端。
- 服務器的返回報文中經過代理服務器同樣新增了一個Via首部。
- 客戶端收到響應后,根據響應正文可以看到起初發送的請求頭與服務器最終收到的請求頭。
OPTIONS
這個請求就很簡單了。當客戶端不確定發起HTTP請求的請求方法時可以使用這個方法詢問服務器對該資源的支持的請求方法,例如:是使用PUT還是使用POST操作等等。
小結:
至此,HTTP的請求報文中的重要的請求方法已經全部講完了,我們經常使用的也就是POS和GET。不過多了解下也沒有壞處,畢竟技多不壓身!下面開始講解請求報文的請求頭。
Request Header(請求頭,也叫請求首部)
先看下一個網頁注冊:
進行網頁注冊的時候,輸入一個一個的key-value形式的值,然后點擊注冊的時候進行HTTP請求。這里我們是進行了Form表單提交動作,把注冊的文本內容發給服務器。那么服務器怎么知道我們發送的內容是文本內容呢?這就需要在請求報文中通過一個標記來標記我這個請求報文是文本編碼的內容。通過請求頭我們服務器可以知道客戶端發送的是什么格式的內容,從而進行相應的處理。
我們在網頁注冊用戶名的時候,
在概論請求報文的請求頭時,我提過一句“請求頭的具體的每一個現成的key的意義在后面會講的”。這句話有三層含義:(O(∩_∩)O哈哈~,開啟語文模式)
- 其一:從“現成”兩字可以看出作者言外之意對于請求頭有先人定義好的供我們使用的,也可以自定義一些請求頭。
- 其二:“key”字表明請求頭是,key-value形式的。
- 其三:沒有其三。
請求頭的語法:
請求頭中的每一個請求頭的key-value均以key開始后面是冒號“:”,然后是該key對應的value值;每一個key-value均以CRLF標志該key-value的結束標語。最終請求頭以一個空行標志請求頭的結束。
一些常見的請求頭
-
Content-Type
這個玩意對應的值用來告訴服務器請求報文的媒體類型。例如:
上面的application/x-www-form-urlencoded是一種文本編碼格式,如果是GET請求,則將請求的數據編碼為name=value&name2=value2的形式拼接在GET請求的請求URL后面傳遞給服務器;如果是POST請求,則將請求的數據放到body中發送給服務器。(故:GET請求沒有請求體,POST有請求體)。
這個玩意又稱之為MIME Type,反正就是用來標記請求報文的類型。更多MIME類型,詳見W3SCHOOL -
Content-Length
用來告知接收端報文數據的大小。這個玩意不僅能用來請求頭中,還可以用在響應頭中用來告訴客戶端響應正文的大小。比如:我要下載一個文件,客戶端與服務端建立連接后,客戶端要進行流操作下載文件,這是客戶端可能就需要Content-Length的值判斷磁盤中是否有足夠的空間大小去存儲要下載的文件。 -
User-Agent
這個就用的更多了。用來告知服務端發起請求的客戶端的設備信息,例如:是手機訪問還是電腦訪問等等。根據訪問設備的不同返回給客戶端不同的網頁。例如手機端的QQ瀏覽器中就有設置User-Agent類型的:
-
Host
客戶端通過 Host 首部為服務器提供客戶端想要訪問的那臺機器的因特網主機名(也就是IP地址)端口號。
例如,Host:www.baidu.com:80 -
Allow
還記得上面的OPTIONS請求么,在響應頭中就有Allow為DELETE,標志對該請求的資源支持的請求方法。
舉一反三,更多請求頭請百度上面Google一下。
首部分類
按照首部的使用在請求頭或者響應頭的地方場景,可以分為:
- 請求首部
- 響應首部
- 通用首部
-
實體首部
下面圖示一下:具體的一些其他的請求首部等等,可以百度。
首部分類
狀態行(也叫響應行)
看下一個狀態行示例:
其對應的標準格式為:
<Http Version>空格<Status Code>空格<Reason Phrase>
Http Version
這個沒什么好說的,目前使用最多的莫過于就是Http 1.1版本了。還有其他的Http 1.2 ;Http 2.0。
Statu Code
常見的已定義的狀態碼,范圍從100~599分為五種狀態碼。
- 100~199 信息性狀態碼
Code | 原因短語 | 意義 |
---|---|---|
100 | Continue | 服務器告訴客戶端收到了請求,請客戶端繼續 |
101 | Switching Protocols | 服務器告知客戶端,其(服務端)正將協議切換為請求頭Update指定的協議 |
- 200~299 成功狀態碼
一般為OK,更多詳見下圖:
300~399 重定向狀態碼
告知客戶端其請求的資源文件已經被轉移了,這是服務器可以用Location返回給客戶端一個地址,讓瀏覽器直接重新連入新的資源鏈接。400~499 客戶端錯誤
最常見的莫過于404 Not Found了。要么是客戶端的請求的URL不存在或者請求報文出錯。500~599 服務器錯誤
常見的500等服務器內部錯誤。
我們其實主要了解一個錯誤的大致原因就可以了,具體原因再進行深入Google。
剩下的響應報文中得消息報頭、響應正文沒什么好講的了。至此,Http報文已經全部講解完了。下面開始了解下報文是如何在客戶端以及服務器端進行傳輸的。
發送傳輸其實想想也就那么幾步,就跟吧大象塞進冰箱里一樣。
HTTP報文的傳輸
先來看個流程:
-
Step1->解析URL
URL在前面介紹過,通過URL的使用DNS進行解析得到對應的IP地址,默認URL的port號為80。 -
Step2->建立TCP連接通道
拿到IP+port后,服務器就與客戶端建立了穩定的TCP連接通道。 -
Step3->發送HTTP報文
發送報文的格式也沒什么好說的了,上面講解報文的時候也是講解的了。至于HTTP報文是如何通過TCP連接發送的。。。這個又是很大的一篇。簡單幾句了解下,HTTP報文通過TCP傳輸是通過流的形式將數據進行傳輸的,在數據傳輸的時候,會把這一大段HTTP報文數據分為一個一個小片段,這個小片段叫做IP分組。
IP分組包括:- IP分組首部
包含著源端和目的端的IP地址一起其他信息。 - TCP分組首部
包含TCP端口號等。 - TCP數據塊
- IP分組首部
端口號和IP地址都知道了,HTTP報文就可在客戶端與服務器端進行傳輸了。
-
Step->4解析HTTP報文
服務器收到報文后,開始解析報文。
解析規則:- 解析請求方法
報文起始行一直向后讀取數據,知道讀取到第一個空格后讀取到的請求頭的請求方法名稱就結束了。 - 每一小節的數據均以空格結束
例如上面讀取到請求方法后繼續向后解析數據,再次遇到空格則URL解析完畢 - 每一行的結束均以CRLF標記代表結束
請求行以CRLF結束后,開始解析首部信息 - 解析首部
首部信息以冒號(:)前面標記key結束;冒號后面直到CRLF代表該key對應的value結束 - 請求頭的結束
請求頭的結束以一個空行用來分割請求頭與請求體。
- 解析請求方法
-
Step->5構建響應數據
服務器根據客戶端的請求構建對應的數據放在響應體中,在使用Content-type標志請求體的MIME,以及其他響應頭。一起返回給客戶端。 -
Step-6>關閉連接
數據傳輸完畢后,關閉TCP連接。
結束語
大雨
先了解了資源在萬網中位置URL;然后是講解了報文的組成:請求行及其構成、請求頭及其構成、狀態行及其構成、消息報頭及其構成還有首部的分類;再然后就是請求方法的分類及其意義;緊接著就是響應報文中的狀態碼;最后了解了一個HTTP數據傳輸的流程。
細雨
OK!真不容易,這篇文章斷斷續續寫了好久。主要是最近公司里加班干項目,時間還緊?;氐郊蚁词戤厱r間就差不多了,再看看資料,充電一會就差不多可以睡了。
言而總之,歡迎拍磚!城里貴,回家蓋房子沒磚啊。。。多讀書,多看報,少吃零食,多睡覺。
PS:
快速定位到簡書主頁。flutterall.com 這年頭買不起房還買不起域名么!!!