HTTP協(xié)議詳解與Android相關(guān)基礎(chǔ)網(wǎng)絡(luò)編程

大量基礎(chǔ)知識(shí)預(yù)警,大神請(qǐng)繞道~~

[TOC]
#### 一.從HTML說起

一.從HTML說起

HTML是Hyper Text Mark-up Language 的縮寫,即“超文本標(biāo)記語言”。注意這幾個(gè)字——“超文本”是指頁面內(nèi)可以包含圖片、鏈接、甚至音樂、程序等非文字元素。"標(biāo)記語言"——是一種將文本(Text)以及文本相關(guān)的其他信息結(jié)合起來,展現(xiàn)出關(guān)于文檔結(jié)構(gòu)和數(shù)據(jù)處理細(xì)節(jié)的計(jì)算機(jī)文字編碼(維基百科),這個(gè)定義難理解的話想想我們熟悉的東西——我們做Android開發(fā)時(shí)寫的xml文件就是標(biāo)記語言,XML全稱為可擴(kuò)展標(biāo)記語言(Extensible Markup Language,簡(jiǎn)稱XML)。
??作為個(gè)人或者組織在萬維網(wǎng)(web)上放置的開始頁面稱為“主頁”,就像這張:

主頁.png

主頁中有指向其他相關(guān)頁面或其他節(jié)點(diǎn)的指針(超鏈接)。所謂超鏈接就是一種統(tǒng)一資源定位器指針(Uniform Resource Locator),也就是傳說中的URL(更通俗一點(diǎn)的說就是“網(wǎng)址”),通過點(diǎn)擊激活它,可使瀏覽器方便快捷的訪問其他的網(wǎng)頁。嗯,這就是一排URL:

URL.png

設(shè)計(jì)HTML 語言的目的是為了能把存放在一臺(tái)電腦中的文本或圖形與另一臺(tái)電腦中的文本或圖形方便地聯(lián)系在一起,形成有機(jī)的整體,人們不用考慮具體信息是在當(dāng)前電腦上還是在網(wǎng)絡(luò)的其它電腦上。這樣,你只要使用鼠標(biāo)在某一文檔中點(diǎn)取一個(gè)圖標(biāo),Internet就會(huì)馬上轉(zhuǎn)到與此圖標(biāo)相關(guān)的內(nèi)容上去,而這些信息可能存放在網(wǎng)絡(luò)的另一臺(tái)電腦中.

二.什么是HTTP

1.什么是HTTP協(xié)議?

協(xié)議是指計(jì)算機(jī)通信網(wǎng)絡(luò)中兩臺(tái)計(jì)算機(jī)之間進(jìn)行通信的所必需共同遵守的規(guī)則。超文本傳輸協(xié)議(HTTP)是一種基于TCP鏈接的通信協(xié)議,是一個(gè)客戶端和服務(wù)端請(qǐng)求和應(yīng)答的標(biāo)準(zhǔn),它允許將超文本標(biāo)記語言(HTML)文檔從web服務(wù)器傳到客戶端的瀏覽器。
??我們想瀏覽一個(gè)網(wǎng)站的時(shí)候,只要在瀏覽器的地址欄里輸入網(wǎng)站的地址就可以了,例如www.baidu.com,但是在瀏覽器的地址欄里面出現(xiàn)的卻是:http://www.baidu.com ,你知道為什么會(huì)多出一個(gè)“http”嗎?
??我們?cè)跒g覽器的地址欄里輸入的網(wǎng)站地址也就是URL。就像每家每戶都有一個(gè)門牌地址一樣,每個(gè)網(wǎng)頁也都有一個(gè)Internet地址。當(dāng)你在瀏覽器的地址框中輸入一個(gè)URL或是單擊一個(gè)超級(jí)鏈接時(shí),URL就確定了要瀏覽的地址。瀏覽器通過超文本傳輸協(xié)議(HTTP),將Web服務(wù)器上站點(diǎn)的網(wǎng)頁代碼提取出來,并翻譯成漂亮的網(wǎng)頁。因此,在我們認(rèn)識(shí)HTTP之前,有必要先弄清楚URL的組成。

2.web服務(wù)器、瀏覽器、代理服務(wù)器

當(dāng)我們打開瀏覽器,在地址欄中輸入U(xiǎn)RL,跳轉(zhuǎn)就會(huì)看到相應(yīng)的網(wǎng)頁。實(shí)際上當(dāng)我們輸入U(xiǎn)RL并敲擊回車之后,我們的瀏覽器給web服務(wù)器發(fā)送了一個(gè)Request,web服務(wù)器接到這個(gè)Request之后做出相應(yīng)的處理,生成相應(yīng)的Response,然后發(fā)送給瀏覽器,瀏覽器解析Response中的HTML,我們就看到了網(wǎng)頁:

圖片1.png

我們的Request有可能是經(jīng)過了代理服務(wù)器的,最后才抵達(dá)Web服務(wù)器:

圖片2.png

代理服務(wù)器(Proxy Server)就是網(wǎng)絡(luò)信息的中轉(zhuǎn)站,其功能就是代理網(wǎng)絡(luò)用戶去獲得網(wǎng)絡(luò)信息,有了它瀏覽器不是直接到web服務(wù)器去取回網(wǎng)頁,而是向代理服務(wù)器發(fā)出Request,由他取回瀏覽器所需要的信息并傳遞給你的瀏覽器。他有以下幾種功能:
①提高訪問速度
??大多數(shù)代理服務(wù)器都有緩存功能。
②突破網(wǎng)絡(luò)限制
??比如局域網(wǎng)對(duì)上網(wǎng)用戶的端口、目的網(wǎng)站、協(xié)議、游戲、即時(shí)通訊軟件等的限制,也就是翻墻了。比如A要訪問C網(wǎng)站,但A到C網(wǎng)絡(luò)出現(xiàn)問題,可以通過繞道,假設(shè)B是代理服務(wù)器,A可通過B, 再由B到C。
③隱藏自己的真實(shí)地址、身份信息
??隱藏自己的IP,防止被黑客攻擊。通過分析指定IP地址,可以查詢到網(wǎng)絡(luò)用戶的目前所在地。代理服務(wù)器知識(shí)是黑客基本功 ,黑客的很多活動(dòng)都是通過代理服務(wù)器, 比如掃描、刺探,對(duì)局域網(wǎng)內(nèi)機(jī)器進(jìn)行滲透,黑客一般攻擊的時(shí)候都是中轉(zhuǎn)了很多級(jí)跳板,才攻擊目標(biāo)機(jī)器。隱藏了身份,保證了自己的安全。

3.URL詳解

超鏈接URL地址用于描述一個(gè)網(wǎng)絡(luò)上的資源,其基本格式如下:

schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]
scheme             指定低層使用的協(xié)議(例如:http, https, ftp)
host                  HTTP服務(wù)器的IP地址或者域名
port#                 HTTP服務(wù)器的默認(rèn)端口是80,這種情況下端口號(hào)可以省略。如果使用了別的端口,必須指明,例如 http://www.cnblogs.com:8080/
path                  訪問資源的路徑
url-params          Url請(qǐng)求參數(shù)
query-string          發(fā)送給http服務(wù)器的數(shù)據(jù)
anchor-               錨

舉個(gè)栗子(這個(gè)網(wǎng)址不知道當(dāng)時(shí)是在哪找的,現(xiàn)在已經(jīng)404了):

http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

從上面的URL可以看出,一個(gè)完整的URL包括以下幾部分:
(1)協(xié)議部分(scheme):該URL的協(xié)議部分為“http:”這代表網(wǎng)頁使用的是HTTP協(xié)議。在Internet中有多種協(xié)議,如HTTP,F(xiàn)TP等等本例中使用的是HTTP協(xié)議?!?/”為分隔符。
(2)域名部分(host):該URL的域名部分為“www.aspxfans.com”,通知aspxfans.com服務(wù)器顯示web。一個(gè)URL中,也可以使用IP地址作為域名使用。
(3)端口部分(port#):跟在域名后面的是端口,域名和端口之間使用“:”作為分隔符。端口不是一個(gè)URL必須的部分,如果省略端口部分,將采用默認(rèn)端口(80),此處的端口號(hào)是8080。
(4)虛擬目錄部分(path訪問資源的路徑):從域名后的第一個(gè)“/”開始到最后一個(gè)“/”為止,是虛擬目錄部分。虛擬目錄也不是一個(gè)URL必須的部分。本例中的虛擬目錄是“/news/”,為該服務(wù)器上的子目錄。可以這么理解,域名對(duì)應(yīng)的服務(wù)器就是C盤,虛擬目錄就是Program Files等子文件夾。
(5)文件名部分(url-params):從域名后的最后一個(gè)“/”開始到“?”為止,是文件名部分,如果沒有“?”,則是從域名后的最后一個(gè)“/”開始到“#”為止,是文件部分,如果沒有“?”和“#”,那么從域名后的最后一個(gè)“/”開始到結(jié)束,都是文件名部分。
??本例中的文件名是“index.asp”,是我們要取的“/news/”文件夾中的一個(gè)HTML網(wǎng)頁。文件名部分也不是一個(gè)URL必須的部分,如果省略該部分,則使用默認(rèn)的文件名。
(6)參數(shù)部分(query-string)從“?”開始到“#”為止之間的部分為參數(shù)部分,又稱搜索部分、查詢部分。本例中的參數(shù)部分為“boardID=5&ID=24618&page=1”。參數(shù)可以允許有多個(gè)參數(shù),參數(shù)與參數(shù)之間用“&”作為分隔符
(7)錨部分(anchor):從“#”開始到最后,都是錨部分。本例中的錨部分是“name”。錨部分也不是一個(gè)URL必須的部分。
??以上例子參考博客:http://blog.csdn.net/ergouge/article/details/8185219,我們可以分析一下這個(gè)博客的url:
該URL采用http協(xié)議,域名為blog.csdn.net,path資源路徑(虛擬目錄)為/ergouge/article/details/,這個(gè)類似于C:\Users\dell.android\avd一層一層的文件夾,而我們要獲取的的“文件”(HTML網(wǎng)頁)id即為8185219。

4.Http協(xié)議工作原理

Web瀏覽器和Web服務(wù)器之間是如何建立連接的呢?主要是通過以下四個(gè)步驟實(shí)現(xiàn)的。
??第一步,在客戶端的瀏覽器中獲取用戶的輸入內(nèi)容。
??第二步,瀏覽器得到網(wǎng)址后,會(huì)將域名發(fā)送到DNS服務(wù)器上,進(jìn)行域名解析,得到目的服務(wù)器的IP地址。
??第三步,使用Socket套接字來實(shí)現(xiàn)TCP/IP鏈接。從瀏覽器到服務(wù)器端口使用的是TCP/IP協(xié)議(網(wǎng)絡(luò)層)來完成的。
??第四步,服務(wù)器的80端口監(jiān)聽客戶端的鏈接,完成客戶端到服務(wù)器的連接。
??上述四個(gè)步驟的具體實(shí)現(xiàn)過程如圖所示。而在Internet內(nèi)部可以通過三種方式來實(shí)現(xiàn)發(fā)送和接收數(shù)據(jù),分別是Http協(xié)議、FTP協(xié)議和TCP/IP協(xié)議。

圖片3.png

服務(wù)器返回客戶端的內(nèi)容有三種形式,分別是:
??(1)以Html代碼的形式返回。
??(2)以xml字符串的形式返回。
??(3)以Json數(shù)據(jù)形式返回,從網(wǎng)絡(luò)流量的角度考慮,Json方式要比xml方式好一些,且便于解析。

三.HTTP的報(bào)文結(jié)構(gòu)

HTTP協(xié)議采用了請(qǐng)求/響應(yīng)模型??蛻舳讼蚍?wù)器發(fā)送一個(gè)請(qǐng)求,請(qǐng)求頭包含請(qǐng)求的方法、URL、協(xié)議版本、以及包含請(qǐng)求修飾符、客戶信息和內(nèi)容的消息結(jié)構(gòu)。服務(wù)器以一個(gè)狀態(tài)行作為響應(yīng),相應(yīng)的內(nèi)容包括消息協(xié)議的版本,成功或者錯(cuò)誤編碼加上包含服務(wù)器信息、實(shí)體元信息以及可能的實(shí)體內(nèi)容。
??通常HTTP消息包括客戶機(jī)向服務(wù)器的請(qǐng)求消息(Request)服務(wù)器向客戶機(jī)的響應(yīng)消息(Respone)。這兩種類型的消息由一個(gè)起始行,一個(gè)或者多個(gè)頭域,一個(gè)指示頭域結(jié)束的空行和可選的消息體組成。HTTP的頭域包括通用頭,請(qǐng)求頭,響應(yīng)頭和實(shí)體頭四個(gè)部分。每個(gè)頭域由一個(gè)域名,冒號(hào)(:)和域值三部分組成。

1.通用頭域

通用頭域包含請(qǐng)求和響應(yīng)消息都支持/包含的頭域,通用頭域包含Cache-Control、Connection、Date、Content-Type、Content-Transfer-Encoding等。對(duì)通用頭域的擴(kuò)展要求通訊雙方都支持此擴(kuò)展,如果存在不支持的通用頭域,一般將會(huì)作為實(shí)體頭域處理。下面簡(jiǎn)單介紹幾個(gè)經(jīng)常使用的通用頭域。
①Cache-Control頭域:
??Cache-Control指定請(qǐng)求和響應(yīng)遵循的緩存機(jī)制。在請(qǐng)求消息或響應(yīng)消息中設(shè)置Cache-Control并不會(huì)修改另一個(gè)消息處理過程中的緩存處理過程。
??請(qǐng)求時(shí)的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached。
??響應(yīng)消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
各個(gè)消息中的指令含義如下:

Public:指示響應(yīng)可被任何緩存區(qū)緩存。
Private:指示對(duì)于單個(gè)用戶的整個(gè)或部分響應(yīng)消息,不能被共享緩存處理。這允許服務(wù)器僅僅描述當(dāng)用戶的部分響應(yīng)消息,此響應(yīng)消息對(duì)于其他用戶的請(qǐng)求無效。
no-cache:指示請(qǐng)求或響應(yīng)消息不能緩存 。
no-store:用于防止重要的信息被無意的發(fā)布。在請(qǐng)求消息中發(fā)送將使得請(qǐng)求和響應(yīng)消息都不使用緩存。
max-age:指示客戶機(jī)可以接收生存期不大于指定時(shí)間(以秒為單位)的響應(yīng)。
min-fresh:指示客戶機(jī)可以接收響應(yīng)時(shí)間小于當(dāng)前時(shí)間加上指定時(shí)間的響應(yīng)。
max-stale:指示客戶機(jī)可以接收超出超時(shí)期間的響應(yīng)消息。如果指定max-stale消息的值,那么客戶機(jī)可以接收超出超時(shí)期指定值之內(nèi)的響應(yīng)消息。

②HTTP Keep-Alive:
??Keep-Alive功能使客戶端到服務(wù)器端的連接持續(xù)有效,當(dāng)出現(xiàn)對(duì)服務(wù)器的后繼請(qǐng)求時(shí),Keep-Alive功能避免了建立或者重新建立連接。對(duì)于提供靜態(tài)內(nèi)容的網(wǎng)站來說,這個(gè)功能通常很有用。
??但是,對(duì)于負(fù)擔(dān)較重的網(wǎng)站來說,這里存在另外一個(gè)問題:雖然為客戶保留打開的連接有一定的好處,但它同樣影響了性能,因?yàn)樵谔幚頃和F陂g,本來可以釋放的資源仍舊被占用。當(dāng)Web服務(wù)器和應(yīng)用服務(wù)器在同一臺(tái)機(jī)器上運(yùn)行時(shí),Keep- Alive功能對(duì)資源利用的影響尤其突出。
③Date頭域:
??Date頭域表示消息發(fā)送的時(shí)間,時(shí)間的描述格式由rfc822定義。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的時(shí)間表示世界標(biāo)準(zhǔn)時(shí),換算成本地時(shí)間,需要知道用戶所在的時(shí)區(qū)。
④Pragma頭域:
??Pragma頭域用來包含實(shí)現(xiàn)特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1協(xié)議中,它的含義和Cache-Control:no-cache相同。

2.Request 請(qǐng)求消息(RequestMessage)

先看Request 消息的結(jié)構(gòu),Request 消息分為3部分,第一部分叫請(qǐng)求行,第二部分叫http header, 第三部分是body。header和body之間有個(gè)空行,結(jié)構(gòu)如下圖:

Request 請(qǐng)求報(bào)文.png

如圖,我們可以看到,一個(gè)完整的Request請(qǐng)求包括三部分——Frist line: GET/POST [URL路徑] HTTP/[HTTP版本]+Header請(qǐng)求頭域+Request Body。下面我們來一一講解講解這三個(gè)重要部分。
(1)請(qǐng)求行:GET/POST [URL路徑] HTTP/[HTTP版本]
??Http協(xié)議定義了很多與服務(wù)器交互的方法,最基本的有4種:GET,POST,PUT,DELETE。 一個(gè)URL地址用于描述一個(gè)網(wǎng)絡(luò)上的資源,而HTTP中的GET, POST, PUT, DELETE就對(duì)應(yīng)著對(duì)這個(gè)資源的查,改,增,刪4個(gè)操作。 我們最常見的就是GETPOST了。
??GET一般用于取回由Request-URI標(biāo)識(shí)的信息(獲取/查詢資源信息)。
??POST一般用于更新資源信息(這里的更新值得是跟原來不一樣,準(zhǔn)確的說是新增。PUT才是嚴(yán)格意義上的修改/跟新),請(qǐng)求服務(wù)器接收包含在請(qǐng)求中的實(shí)體信息??梢杂糜谔峤槐韱?,向新聞組、BBS、郵件群組和數(shù)據(jù)庫發(fā)送消息。
我們看看GET和POST的區(qū)別:
??①GET提交的數(shù)據(jù)會(huì)放在URL之后,以“?”分割URL和傳輸數(shù)據(jù),參數(shù)之間以“&”相連,如EditPosts.aspx?name=test1&id=123456;POST方法是把提交的數(shù)據(jù)放在HTTP數(shù)據(jù)包的Body中。
??②GET提交的數(shù)據(jù)就在URL中,所以大小有限制(因?yàn)闉g覽器對(duì)URL的長(zhǎng)度有限制);POST方法提交的數(shù)據(jù)沒有限制。
??③GET方式需要使用Request.QueryString來取得變量的值;POST方式通過Request.Form來獲取變量的值。
??④GET方式提交數(shù)據(jù),會(huì)帶來安全問題,比如一個(gè)登錄頁面,通過GET方式提交數(shù)據(jù)時(shí),用戶名和密碼將出現(xiàn)在URL上,如果頁面可以被緩存或者其他人可以訪問這臺(tái)機(jī)器,就可以從歷史記錄獲得該用戶的賬號(hào)和密碼。
(2)Header請(qǐng)求頭域
??請(qǐng)求頭域允許客戶端向服務(wù)器傳遞關(guān)于客戶端的附加信息,請(qǐng)求頭域可能包含下列字段(筆者就整理了這么多,歡迎補(bǔ)充):

1)Accept:設(shè)置服務(wù)器返回的數(shù)據(jù)類型。
2)Accept-Languge:設(shè)置服務(wù)器返回的語言。
3)Accept-Encoding:設(shè)置服務(wù)器返回的壓縮編碼。
4)Accept-Charset:設(shè)置服務(wù)器返回的文字編碼。
5)User-Agent:請(qǐng)求類型唯一標(biāo)識(shí)。
6)Host頭域 : Host頭域指定請(qǐng)求資源的Intenet主機(jī)和端口號(hào),必須表示請(qǐng)求url的原始服務(wù)器或網(wǎng)關(guān)的位置。HTTP/1.1請(qǐng)求必須
    包含主機(jī)頭域,否則系統(tǒng)會(huì)以400狀態(tài)碼返回。
7)Referer頭域 : Referer頭域允許客戶端指定請(qǐng)求uri的源資源地址,這可以允許服務(wù)器生成回退鏈表,可用來登陸、優(yōu)化cache
    等。他也允許廢除的或錯(cuò)誤的連接由于維護(hù)的目的被追蹤。如果請(qǐng)求的uri沒有自己的uri地址,Referer不能被發(fā)送。如果指
    定的是部分URL地址,則此地址應(yīng)該是一個(gè)相對(duì)地址。
8)User-Agent頭域:User-Agent頭域的內(nèi)容包含發(fā)出請(qǐng)求的用戶信息。
9)Range頭域: Range頭域可以請(qǐng)求實(shí)體的一個(gè)或者多個(gè)子范圍。
        例如, 表示頭500個(gè)字節(jié):bytes=0-499
        表示第二個(gè)500字節(jié):bytes=500-999
        表示最后500個(gè)字節(jié):bytes=-500
        表示500字節(jié)以后的范圍:bytes=500-
        第一個(gè)和最后一個(gè)字節(jié):bytes=0-0,-1
        同時(shí)指定幾個(gè)范圍:bytes=500-600,601-999
        但是服務(wù)器可以忽略此請(qǐng)求頭,如果無條件GET包含Range請(qǐng)求頭,響應(yīng)會(huì)以狀態(tài)碼206(PartialContent)返回而不是200(OK)。

(3)Request Body(只有POST請(qǐng)求的Body有意義)
??注意是POST,如果是GET的話這部分是空的,因?yàn)镚ET只是單純的請(qǐng)求獲取一些東西,他的參數(shù)都在URL中的那個(gè)"?"后面標(biāo)明了;而POST則是要往服務(wù)器上發(fā)送一些東西(用于數(shù)據(jù)更新等),這個(gè)body就是我們發(fā)送的東西

3.Response應(yīng)答消息

我們?cè)倏碦esponse消息的結(jié)構(gòu), 和Request消息的結(jié)構(gòu)基本一樣。 同樣也分為三部分,第一部分叫request line(請(qǐng)求行), 第二部分叫request header(請(qǐng)求頭),第三部分是Respone body。 header和body之間也有個(gè)空行,結(jié)構(gòu)如下圖:

Respone報(bào)文格式.png

Response 消息中的第一行叫做狀態(tài)行,由HTTP協(xié)議版本號(hào),狀態(tài)碼,狀態(tài)消息三部分組成。狀態(tài)碼和狀態(tài)信息也就是上面表格中的200 OK,HTTP/1.1中有5類狀態(tài)碼,狀態(tài)碼由三位數(shù)字組成,第一個(gè)數(shù)字定義了響應(yīng)的類別:

    1XX  提示信息 - 表示請(qǐng)求已被成功接收,繼續(xù)處理
  2XX  成功 - 表示請(qǐng)求已被成功接收,理解,接受
  3XX  重定向 - 要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的處理
  4XX  客戶端錯(cuò)誤 -  請(qǐng)求有語法錯(cuò)誤或請(qǐng)求無法實(shí)現(xiàn)
  5XX  服務(wù)器端錯(cuò)誤 -   服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求
  
看看一些常見的狀態(tài)碼:
    200 OK:最常見的就是成功響應(yīng)狀態(tài)碼200了, 這表明該請(qǐng)求被成功地完成,所請(qǐng)求的資源發(fā)送回客戶端。
    302 Found:重定向,新的URL會(huì)在response中的Location中返回,瀏覽器將會(huì)使用新的URL發(fā)出新的Request。
        例如在IE中輸入http://www.google.com. HTTP服務(wù)器會(huì)返回304, IE取到Response中Location header的新URL, 又重新發(fā)送了一個(gè)Request.
    304 Not Modified:代表上次的文檔已經(jīng)被緩存了, 還可以繼續(xù)使用,
        提示:如果你不想使用本地緩存可以用Ctrl+F5強(qiáng)制刷新頁面
    400 Bad Request  客戶端請(qǐng)求與語法錯(cuò)誤,不能被服務(wù)器所理解
    403 Forbidden  服務(wù)器收到請(qǐng)求,但是拒絕提供服務(wù)
    404 Not Found  請(qǐng)求資源不存在(輸錯(cuò)了URL)

這里我們?cè)俳忉屢幌翿esponse消息中的請(qǐng)求頭中幾個(gè)常見參數(shù):

1)Content-Type:是HTTP協(xié)議header中一個(gè)重要的參數(shù),它用于標(biāo)識(shí)發(fā)送或接收到的數(shù)據(jù)的類型,缺省值為" text/plain"。瀏
    覽器根據(jù)該參數(shù)來決定數(shù)據(jù)的打開方式。Content-Type使用的是 “主類型/子類型; 額外參數(shù)” ([type]/[subtype]; 
    parameter )的數(shù)值格式。主要類型有9種,分別是application、audio、example、image、message、model、
    multipart、text、video。
            “子類型” (subtype)用于指定"主類型"的詳細(xì)形式。 其中以x-開頭表示該類別尚未標(biāo)準(zhǔn)化。當(dāng)客戶端不能確定“子
    類型”時(shí),會(huì)根據(jù)“主類型”來獲取默認(rèn)的子類型。 "額外參數(shù)" (parameter)用于指定請(qǐng)求/響應(yīng)內(nèi)容的字符編碼格式。例如
    text/html;charset=utf-8;
        ①text:用于標(biāo)準(zhǔn)化地表示的文本信息,文本消息可以是多種字符集和或者多種格式的;默認(rèn)是text/plain; 
            text/plain:純文本,文件擴(kuò)展名.txt
            text/html:HTML文本,文件擴(kuò)展名.htm和.html
        ②multipart:用于連接消息體的多個(gè)部分構(gòu)成一個(gè)消息,這些部分可以是不同類型的數(shù)據(jù); 默認(rèn)是multipart/mixed;
        ③image:用于傳輸靜態(tài)圖片數(shù)據(jù);
            image/jpeg:jpeg格式的圖片,文件擴(kuò)展名.jpg
            image/gif:GIF格式的圖片,文件擴(kuò)展名.gif
        ④audio:用于傳輸音頻或者音聲數(shù)據(jù);
            audio/x-wave:WAVE格式的音頻,文件擴(kuò)展名.wav
            audio/mpeg:MP3格式的音頻,文件擴(kuò)展名.mp3
        ⑤video:用于傳輸動(dòng)態(tài)影像數(shù)據(jù),可以是與音頻編輯在一起的視頻數(shù)據(jù)格式。
            video/mpeg:MPEG格式的視頻,文件擴(kuò)展名.mpg
        ⑥application:用于傳輸應(yīng)用程序數(shù)據(jù)或者二進(jìn)制數(shù)據(jù); 默認(rèn)是application/octet-stream; 
            application/zip:PK-ZIP格式的壓縮文件,文件擴(kuò)展名.zip
        ⑦message:用于包裝一個(gè)E-mail消息;
2)Content-transfer-encoding:這條語句指明了編碼轉(zhuǎn)換的方式。Content-transfer-encoding的值有5種----
    "7bit"、"8bit"、"binary"、"quoted-printable"和"base64"----其中"7bit"是缺省值,即不用轉(zhuǎn)化的ASCII字符。
3)Content-Length:返回?cái)?shù)據(jù)流內(nèi)容長(zhǎng)度
4)Accept-Ranges:返回?cái)?shù)據(jù)流壓縮編碼
5)Server:服務(wù)器類型

四.Android相關(guān)基礎(chǔ)網(wǎng)絡(luò)編程

之所以說是Android基礎(chǔ)網(wǎng)絡(luò)編程,是因?yàn)槲覀冎v的東西都是比較原始的網(wǎng)絡(luò)連接方式。真正的開發(fā)中我們要考慮很多性能問題,比如多線程等。我們常用的網(wǎng)絡(luò)請(qǐng)求庫如OKHttp等也就是封裝的下面的內(nèi)容,只不過考慮了很多性能問題,做了優(yōu)化封裝。

1.Android網(wǎng)絡(luò)編程分為兩種:基于HTTP協(xié)議與基于Socket

在計(jì)算機(jī)網(wǎng)絡(luò)知識(shí)體系中,運(yùn)輸層的TCP(傳輸控制協(xié)議)把連接作為最基本的抽象。TCP的連接有兩個(gè)端點(diǎn),被稱為Socket,通過IP地址+端口號(hào)來區(qū)分來自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。Client進(jìn)程和Server進(jìn)程之間是通過Socket讀寫數(shù)據(jù)進(jìn)行通信的。
??JDK的java.net包下有兩個(gè)類:Socket和ServerSocket,在Client和Server建立連接成功后,兩端都會(huì)產(chǎn)生一個(gè)Socket實(shí)例,操作這個(gè)實(shí)例,完成所需的會(huì)話,而程序員就通過這些API進(jìn)行網(wǎng)絡(luò)編程。 Socket連接過程分為三個(gè)步驟:服務(wù)器監(jiān)聽,客戶端請(qǐng)求,連接確認(rèn)。Socket是對(duì)TCP/IP協(xié)議的封裝,他本身不是什么協(xié)議,而是一個(gè)調(diào)用接口(API)。TCP/IP只是一個(gè)協(xié)議棧,必須要具體實(shí)現(xiàn),同時(shí)還要提供對(duì)外的操作接口,這就是Socket接口。通過Socket,我們才能使用TCP/IP協(xié)議,因此有了一系列我們知道的函數(shù)接口——connect、accept、send、read、write等。
??HTTP是應(yīng)用層的協(xié)議,主要解決如何包裝數(shù)據(jù),應(yīng)用層的協(xié)議有很多,除了HTTP協(xié)議以外還有FTP、TELNET等。Web使用HTTP協(xié)議作為應(yīng)用層協(xié)議,以封裝HTTP文本信息,然后使用TCP/IP做傳輸層協(xié)議將其發(fā)送到網(wǎng)絡(luò)上。我們?cè)趥鬏敂?shù)據(jù)時(shí)可以只使用(傳輸層)TCP/IP協(xié)議,但那樣的話,如果沒有應(yīng)用層,便無法識(shí)別數(shù)據(jù)內(nèi)容。
??注意Socket對(duì)TCP/IP協(xié)議的封裝是跨傳輸層和應(yīng)用層的(不然在客戶端也無法調(diào)用)。
??由于通常情況下Socket鏈接即是TCP鏈接,因此Socket一旦建立,通信雙方即可開始相互發(fā)送數(shù)據(jù),直到雙方鏈接斷開,即不需要客戶端給服務(wù)器發(fā)送請(qǐng)求,服務(wù)器端也可以主動(dòng)向客戶端發(fā)送數(shù)據(jù);而HTTP鏈接使用的是“請(qǐng)求—響應(yīng)”模式,不僅在請(qǐng)求時(shí)需要建立連接,而且客戶端向服務(wù)器發(fā)出請(qǐng)求后服務(wù)器端才會(huì)回復(fù)數(shù)據(jù),因此使用HTTP協(xié)議的客戶端需要一個(gè)“心跳包”去定時(shí)詢問服務(wù)器是否有新的數(shù)據(jù)并獲取。
??因此,Socket看起來是比較方便的。但是他的缺點(diǎn)也是很明顯的:客戶端到服務(wù)器之間往往是要穿過多個(gè)中間點(diǎn)的,比如路由器、網(wǎng)關(guān)、防火墻等,很多防火墻會(huì)默認(rèn)關(guān)閉長(zhǎng)時(shí)間處于非活躍狀態(tài)的鏈接,所以長(zhǎng)時(shí)間沒有數(shù)據(jù)更新的話很容易導(dǎo)致Socket鏈接中斷,因此需要輪詢告訴網(wǎng)絡(luò)這個(gè)該鏈接處于活躍狀態(tài),利用Socket的客戶端實(shí)現(xiàn)成本更高一些。
??一般來講,像QQ、微信這種即時(shí)通訊類型的客戶端就需要用Socket,而像一般論壇或者微博之類的用HTTP協(xié)議就可以了。

2.實(shí)現(xiàn)Android端的基于HTTP協(xié)議網(wǎng)絡(luò)傳輸

在Android中進(jìn)行http傳輸可以使用HttpURLConnection或者HttpClient類連接URL,但是HttpClient谷歌已經(jīng)給他廢了,所以我們只講HttpURLConnection。
(1)“GET”方法:

GET讀取網(wǎng)頁流程.png

注:上述流程也是一般的網(wǎng)頁爬蟲爬取數(shù)據(jù)的流程。
第一步:指定URL資源并實(shí)現(xiàn)連接:

URL url = new URL(http://www.baidu.com);          //創(chuàng)建一個(gè)URL對(duì)象:
HttpURLConnection conn = (HttpURLConnection)url.openConnection(); //打開一個(gè)鏈接,用HttpURLConnection對(duì)象conn從網(wǎng)絡(luò)中獲取網(wǎng)頁數(shù)據(jù)。

面這句代碼這里實(shí)際上包含了兩步:
URLConnection urlConnection = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;
??URLConnection 與HttpURLConnection 使用的都是Java.net中的類,屬于Java標(biāo)準(zhǔn)接口,后者繼承自前者,差別在于后者只適用于http協(xié)議。從這里我們可以看出實(shí)際上 實(shí)現(xiàn)網(wǎng)絡(luò)連接的是URLConnection類,這里是把這個(gè)類的對(duì)象強(qiáng)制轉(zhuǎn)化為了HttpURLConnection類型,具體知識(shí)可以了解JAVA網(wǎng)絡(luò)編程方面的知識(shí)。
第二步:設(shè)置連接參數(shù)的方法:

conn.setConnectTimeout(6*1000);     //設(shè)置連接超時(shí),如果網(wǎng)絡(luò)不好,Android系統(tǒng)會(huì)在設(shè)置的時(shí)間內(nèi)收回資源終端操作
conn.setDoOutput(false);           //如果是POST請(qǐng)求,參數(shù)在正文中所以要允許輸出流,如果是get請(qǐng)求不用輸出了因?yàn)閰?shù)是在URL之后。
conn.setDoInput(true);                //響應(yīng)數(shù)據(jù)的接收,所以我們要允許數(shù)據(jù)輸入流。
conn.setUesCaches(false); //設(shè)置不用緩存,否則文件過的大的時(shí)候可能會(huì)造成內(nèi)存溢出。
conn.setRequestMethod(“GET”);   //設(shè)置請(qǐng)求方式,注意不設(shè)的話默認(rèn)情況下也是GET,因此setDoOutput默認(rèn)情況下是false,setDoInput默認(rèn)是true。
conn.setRequestProperty(
              URLEncoder.encode("Content-type","UTF-8"),
              URLEncoder.encode("application/x-java-serialized-object","UTF-8")
          );    //setRequestProperty這個(gè)函數(shù)中的參數(shù)也就是上面的Content-Type參數(shù)。

第三步:建立和遠(yuǎn)程資源之間的實(shí)際連接

conn.connect();              //建立一個(gè)TCP連接,這里直接封裝好了,不用三次握手。
            //注意第二步中的那些屬性都必須在connect()之前。

第四步:接收請(qǐng)求響應(yīng)數(shù)據(jù):
??對(duì)于GET請(qǐng)求,不用管outputStream,調(diào)用conn.getInputStream()方法來創(chuàng)建輸入流即可接收Http的請(qǐng)求響應(yīng)。如果是使用POST方法則需要調(diào)用conn.getOutputStream()方法來創(chuàng)建輸出流,通過輸出流將Http請(qǐng)求實(shí)體中的請(qǐng)求參數(shù)傳遞到服務(wù)器上。

InputStream inStream = conn.getInputStream();
InputStreamReader in = new InputStreamReader(inStream );    //無論是GET還是POST,HTTP的請(qǐng)求實(shí)際上直到調(diào)用
        //coon.getInputStream()才真正發(fā)送出去,對(duì)于GET而言不需要outputSream操作,因?yàn)橐呀?jīng)DoOutput(false)了。
BufferedReader buffer = new BufferedReader(in);
If(conn.getResponseCode()!= 200){                  //對(duì)響應(yīng)碼進(jìn)行判斷
     Throw new RuntimeException(“請(qǐng)求URL失敗”);
}else{
     //輸入流讀取字節(jié),再將它們轉(zhuǎn)化成字符,讀取內(nèi)容:
     String inputLine = null;
     String resultData = null;
     While((inputLine = buffer.readeLine())!=null){
           resultData += inputLine;        //利用循環(huán)來讀取數(shù)據(jù)
     }
}
conn.disconnect();                        //及時(shí)關(guān)閉
(2)"POST”方法

POST用法與GET方法基本一致,不同的是POST是要往服務(wù)器端發(fā)送數(shù)據(jù)(用于數(shù)據(jù)更新等),它的參數(shù)是放在HTTP正文內(nèi)的,所以這個(gè)時(shí)候就要用到conn.getOutputStream();方法。
??還有就是GET請(qǐng)求可以緩存(當(dāng)然也可以手動(dòng)不緩存),Post請(qǐng)求不能緩存——至于為什么不能緩存,這個(gè)過于博大精深,有待于進(jìn)一步研究。
第一步:指定URL資源并實(shí)現(xiàn)連接

URL url = new URL(http://localhost:8080/TestHttpURLConnectionPro.do);     
HttpURLConnection conn = (HttpURLConnection)url.openConnection();

第二步:指定鏈接參數(shù)的方法

conn.setDoOutput(true);      //設(shè)置是否向HttpURLConnection輸出,這個(gè)是post請(qǐng)求,參數(shù)要放在http協(xié)議正文中,所以必須設(shè)為true,默認(rèn)狀態(tài)下為false.
conn.setDoInput(true);      //設(shè)置是否從HttpURLConnection讀入信息。
conn.setUesCaches(false);   //POST不能用緩存
conn.setRequestProperty(“Content-type”,”application/x-iava-serialized-object”); //上面這一步也要看我們與服務(wù)器之間的約定
conn.setRequestMethod(“POST”); 

第三步:建立和遠(yuǎn)程資源之間的實(shí)際連接
??OutputStream不是一個(gè)網(wǎng)絡(luò)流,充其量只是一個(gè)字符串流,往其中寫的東西不會(huì)立即發(fā)送到網(wǎng)絡(luò)上,而是存在于內(nèi)存緩存區(qū),等到outputStream流關(guān)閉時(shí),根據(jù)其內(nèi)容生成HTTP正文
??至此,http請(qǐng)求的東西已經(jīng)全部準(zhǔn)備好了,在第四步調(diào)用getInputStream()函數(shù)調(diào)用的時(shí)候,就會(huì)把準(zhǔn)備好的http請(qǐng)求發(fā)送到服務(wù)器,并且返回一個(gè)輸入流,用于讀取服務(wù)器對(duì)于此次請(qǐng)求的返回信息。
??由于http請(qǐng)求在getInputStream()函數(shù)調(diào)用的時(shí)候已經(jīng)發(fā)送出去了(包括http頭和正文body),因此在這之后對(duì)conn對(duì)象進(jìn)行設(shè)置(對(duì)http頭的信息修改)或者寫入outputStream(對(duì)正文進(jìn)行修改)都是沒有意義的,執(zhí)行這些操作會(huì)發(fā)生異常。

conn.connect();
OutputStream outStream = conn.getOutputStream();    //此處OutputStream會(huì)隱含進(jìn)行connect,所以上面的connect()方法可以不用寫。
DataOutputStream out= new DataOutputStream (outStream );

第四步:寫入并發(fā)送我們的實(shí)際數(shù)據(jù)

String content = “firstname”+URLEncoder.encode(“LIU”,”UTF-8”);
out.writeBytes(content);   //向?qū)ο罅鲗懭霐?shù)據(jù),這些數(shù)據(jù)將存到內(nèi)存緩沖區(qū)
out.flush();       //刷新對(duì)象輸出流,將任何字節(jié)都寫入潛在流中。
out.close();      //關(guān)閉流對(duì)象,此時(shí)不能再向?qū)ο罅鲗懭肴魏螖?shù)據(jù),先前寫入的數(shù)據(jù)存在于內(nèi)存緩沖區(qū)中,在調(diào)用下邊的getInputStream()函數(shù)時(shí)才把準(zhǔn)備好的請(qǐng)求發(fā)送到服務(wù)器。

InputStream inStream = conn.getInputStream();    //注意啦,這里和GET請(qǐng)求中的一樣
InputStreamReader in = new InputStreamReader(inStream) ;//getInputStream()這個(gè)函數(shù)的作用有兩個(gè),一個(gè)是發(fā)送
                //請(qǐng)求(包括上面我們寫入的數(shù)據(jù)),另一個(gè)是讀取響應(yīng),即便是POST,你給服務(wù)器發(fā)送數(shù)據(jù)之后他還是要給你
                //傳一些數(shù)據(jù)回來的,不如說更新成功之類的。
BufferedReader buffer = new BufferReader(in);
if(conn.getResponseCode()!= 200){
     Throw new RuntimeException(“請(qǐng)求URL失敗”);
}else{
     String inputLine = null;
     String resultData = null;
     While((inputLine = buffer.readeLine())!=null){
           resultData += inputLine;
     }
}
conn.disconnect();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,741評(píng)論 6 152
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng),內(nèi)容來源于博客園作者M(jìn)IN飛翔的HTTP協(xié)...
    Sivin閱讀 5,345評(píng)論 3 82
  • PS:簡(jiǎn)書的網(wǎng)址真不是給人看的。。。我單獨(dú)開了一個(gè)網(wǎng)址可以重定向到我的簡(jiǎn)書主頁。博客地址:flutterall.c...
    徐愛卿閱讀 7,116評(píng)論 21 97
  • 本文整理自MIN飛翔博客 [1] 1. 概念 協(xié)議是指計(jì)算機(jī)通信網(wǎng)絡(luò)中兩臺(tái)計(jì)算機(jī)之間進(jìn)行通信所必須共同遵守的規(guī)定或...
    HoyaWhite閱讀 2,795評(píng)論 2 20

友情鏈接更多精彩內(nèi)容