HTTP網(wǎng)絡(luò)請(qǐng)求
對(duì)于android開(kāi)發(fā)來(lái)說(shuō),http是網(wǎng)絡(luò)開(kāi)發(fā)中最為重要鳖擒、使用頻率最高的手段粒竖。
HTTP請(qǐng)求原理
http是一種應(yīng)用層協(xié)議照捡,它通過(guò)tcp實(shí)現(xiàn)了可靠的數(shù)據(jù)傳輸。詳細(xì)的交互流程如下:
- 客戶端執(zhí)行網(wǎng)絡(luò)請(qǐng)求击狮,從url中解析出服務(wù)器的主機(jī)名
- 將服務(wù)器的主機(jī)名轉(zhuǎn)換成服務(wù)器ip地址
- 將端口號(hào)從url中解析出來(lái)
- 建立一條客戶端與服務(wù)器的tcp鏈接
- 客戶端通過(guò)輸入流向服務(wù)器發(fā)送一條http請(qǐng)求
- 服務(wù)器向客戶端回送一條http響應(yīng)報(bào)文
- 客戶端從輸入流獲取報(bào)文
- 解析報(bào)文佛析,關(guān)閉連接
HTTP請(qǐng)求方式
- get請(qǐng)求(查)
- post請(qǐng)求(改)
- delete請(qǐng)求(刪)
- put請(qǐng)求(增)
- head請(qǐng)求:服務(wù)器只返回首部,不會(huì)返回實(shí)體的主體部分彪蓬。
- trace請(qǐng)求:客戶端發(fā)起一個(gè)請(qǐng)求寸莫,可能需要通過(guò)防火墻、代理档冬、網(wǎng)關(guān)或其他一些應(yīng)用程序膘茎。每個(gè)節(jié)點(diǎn)都可能修改原始的請(qǐng)求。trace請(qǐng)求會(huì)有目的服務(wù)器發(fā)起一個(gè)環(huán)回診斷酷誓,行程最后一站的服務(wù)器會(huì)彈回一條trace響應(yīng)披坏,并在響應(yīng)主體中攜帶它收到的原始請(qǐng)求報(bào)文。
- options請(qǐng)求:請(qǐng)求web服務(wù)器告知其支持的各種功能盐数。
HTTP報(bào)文格式解析
請(qǐng)求報(bào)文
通常來(lái)說(shuō)HTTP請(qǐng)求報(bào)文由請(qǐng)求行(request line)棒拂、請(qǐng)求頭部(header)、空行和請(qǐng)求數(shù)據(jù)4個(gè)部分組成:
get請(qǐng)求:
GET /search?hl=zh-CN&source=hp&q=domety&aq=f&oq= HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/msword, application/x-silverlight, application/x-shockwave-flash, /
Referer: <a >http://www.google.cn/</a>
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld)
Host: <a >www.google.cn</a>
Connection: Keep-Alive
Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g;
NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-
FxlRugatx63JLv7CWMD6UB_O_r
post請(qǐng)求:
POST /api/feed/ HTTP/1.1
Accept-Encoding:gzip
Content-Length:225873
Content-Type:multipart/form-data; boundary=OCqxMF6-ajdifjiqen
Host:www.myhost.com
Connection:Keep-Alive--OCqxMF6-ajdifjiqen
Content-Disposition:form-data;name="username"
Content-Type:text/plain;charset=UTF-8
Content-Transfer-Endcoding:8bitMr.Simple
--OCqxMF6-ajdifjiqen
Content-Disposition:form-data;name="username"
Content-Type:text/plain;charset=UTF-8
Content-Transfer-Endcoding:8bitMr.Simple
響應(yīng)報(bào)文
HTTP響應(yīng)也由三個(gè)部分組成玫氢,分別是:狀態(tài)行帚屉、消息報(bào)文谜诫、響應(yīng)正文。狀態(tài)行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
其中涮阔,HTTP-Version表示服務(wù)器HTTP協(xié)議的版本猜绣;Status-Code表示服務(wù)器發(fā)回的響應(yīng)狀態(tài)代碼;Reason-Phrase表示狀態(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)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn)多搀。
5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。
常見(jiàn)狀態(tài)代碼灾部、狀態(tài)描述的說(shuō)明如下康铭。
200 OK:客戶端請(qǐng)求成功。
400 Bad Request:客戶端請(qǐng)求有語(yǔ)法錯(cuò)誤赌髓,不能被服務(wù)器所理解从藤。
401 Unauthorized:請(qǐng)求未經(jīng)授權(quán),這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用锁蠕。
403 Forbidden:服務(wù)器收到請(qǐng)求夷野,但是拒絕提供服務(wù)。
404 Not Found:請(qǐng)求資源不存在荣倾,舉個(gè)例子:輸入了錯(cuò)誤的URL悯搔。
500 Internal Server Error:服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤。
503 Server Unavailable:服務(wù)器當(dāng)前不能處理客戶端的請(qǐng)求舌仍,一段時(shí)間后可能恢復(fù)正常鳖孤,舉個(gè)例子:HTTP/1.1 200 OK(CRLF)。
示例:
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 122<html>
<head>
<title>W(wǎng)rox Homepage</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>
常見(jiàn)的請(qǐng)求頭部:
典型的請(qǐng)求頭部有:
- Content-Type:請(qǐng)求數(shù)據(jù)的格式
- Conteent-Lenth:消息長(zhǎng)度
- Host:請(qǐng)求的主機(jī)名
- User-Agent:發(fā)出請(qǐng)求的瀏覽器類型
- Accept:客戶端可以識(shí)別的內(nèi)容類型列表
- Accept-Encoding:客戶端可識(shí)別的數(shù)據(jù)編碼
- Connection:允許客戶端 和服務(wù)器指定與請(qǐng)求/響應(yīng)連接有關(guān)的選項(xiàng)
- Transfer-Encoding:為了保證可靠傳輸抡笼,對(duì)報(bào)文采用了什么編碼方式
代碼實(shí)現(xiàn)
服務(wù)端:
- 建立socket監(jiān)聽(tīng),循環(huán)監(jiān)聽(tīng)端口黄鳍,收到連接請(qǐng)求進(jìn)入新線程處理
- 獲取輸入流推姻,解析請(qǐng)求(header、參數(shù))
- 處理框沟、返回請(qǐng)求結(jié)果
- 關(guān)閉輸入藏古、輸出流增炭、客戶端socket連接
http:// 的 HTTP 協(xié)議交給 java.net.HttpURLConnection 的子類
sun.net.www.protocol.http.HttpURLConnection 處理
ftp:// 的 FTP 協(xié)議交給 URLConnection 的子類 sun.net.www.protocol.ftp.FtpURLConnection 處理
file:// 的 FILE 協(xié)議交給 URLConnection 的子類 sun.net.www.protocol.file.FileURLConnection 處理
https:// 的 HTTPS 協(xié)議交給 javax.net.ssl.HttpsURLConnection 的子類 sun.net.www.protocol.https.HttpsURLConnectionImpl 處理
com.sun 和 sun 開(kāi)頭包名的類都是 JRE 的底層,被找包在 rt.jar 這個(gè) jar 包中拧晕。雖然在 Eclipse 中可以使用反編譯工具打開(kāi)隙姿,但是如果想要看這些類帶有注釋的源代碼的話,應(yīng)該去下載 JDK 所有的源代碼厂捞。
HTTP緩存
http://my.oschina.net/leejun2005/blog/369148
cookie&session
Cookie或Session技術(shù)的應(yīng)用输玷,解決了HTTP協(xié)議的一個(gè)問(wèn)題 -- 無(wú)法保持客戶狀態(tài)
cookie是什么
坦白的說(shuō),一個(gè)cookie就是存儲(chǔ)在用戶主機(jī)瀏覽器中的一小段文本文件靡馁。Cookies是純文本形式欲鹏,它們不包含任何可執(zhí)行代碼。一個(gè)Web頁(yè)面或服務(wù)器告之瀏覽器來(lái)將這些信息存儲(chǔ)并且基于一系列規(guī)則在之后的每個(gè)請(qǐng)求中都將該信息返回至服務(wù)器臭墨。Web服務(wù)器之后可以利用這些信息來(lái)標(biāo)識(shí)用戶赔嚎。多數(shù)需要登錄的站點(diǎn)通常會(huì)在你的認(rèn)證信息通過(guò)后來(lái)設(shè)置一個(gè)cookie,之后只要這個(gè)cookie存在并且合法胧弛,你就可以自由的瀏覽這個(gè)站點(diǎn)的所有部分尤误。再次,cookie只是包含了數(shù)據(jù)结缚,就其本身而言并不有害损晤。
創(chuàng)建cookie
通過(guò)HTTP的Set-Cookie消息頭,Web服務(wù)器可以指定存儲(chǔ)一個(gè)cookie掺冠。Set-Cookie消息的格式如下面的字符串(中括號(hào)中的部分都是可選的)沉馆;瀏覽器把cookie通過(guò)HTTP Request中的“Cookie: header”發(fā)送給Web服務(wù)器。
Set-Cookie:value [ ;expires=date][ ;domain=domain][ ;path=path][ ;secure]
消息頭的第一部分德崭,value部分斥黑,通常是一個(gè)name=value格式的字符串。事實(shí)上眉厨,原始手冊(cè)指示這是應(yīng)該使用的格式锌奴,但是瀏覽器對(duì)cookie的所有值并不會(huì)按此格式校驗(yàn)。實(shí)際上憾股,你可以指定一個(gè)不包含等號(hào)的字符串并且它同樣會(huì)被存儲(chǔ)鹿蜀。然而,通常性的使用方式是以name=value的格式(并且多數(shù)的接口只支持該格式)來(lái)指定cookie的值服球。
當(dāng)一個(gè)cookie存在茴恰,并且可選條件允許的話,該cookie的值會(huì)在接下來(lái)的每個(gè)請(qǐng)求中被發(fā)送至服務(wù)器斩熊。cookie的值被存儲(chǔ)在名為Cookie的HTTP消息頭中往枣,并且只包含了cookie的值,其它的選項(xiàng)全部被去除。例如:
Cookie : value
通過(guò)Set-Cookie指定的選項(xiàng)只是應(yīng)用于瀏覽器端分冈,一旦選項(xiàng)被設(shè)置后便不會(huì)被服務(wù)器重新取回圾另。cookie的值與Set-Cookie中指定的值是完全一樣的字符串;對(duì)于這些值不會(huì)有更近一步的解析或轉(zhuǎn)碼操作雕沉。如果在指定的請(qǐng)求中有多個(gè)cookies集乔,那么它們會(huì)被分號(hào)和空格分開(kāi),例如:
Cookie:value1 ; value2 ; name1=value1
有效期選項(xiàng)(The expires option)
緊跟cookie值后面的每個(gè)選項(xiàng)都以分號(hào)和空格分割坡椒,并且每個(gè)選項(xiàng)都指定cookie何時(shí)應(yīng)該被發(fā)送到服務(wù)器扰路。第一個(gè)選項(xiàng)是expires,其指定了cookie何時(shí)不會(huì)再被發(fā)送到服務(wù)器端的肠牲,因此該cookie可能會(huì)被瀏覽器刪掉幼衰。該選項(xiàng)所對(duì)應(yīng)的值是一個(gè)格式為Wdy,DD-Mon--YYYY HH:MM:SS GMT的值,例如:
Set-Cookie:name=Nicholas;expires=Sat, 02 May 2009 23:38:25 GMT
在沒(méi)有expires選項(xiàng)時(shí),cookie的壽命僅限于單一的會(huì)話中。瀏覽器的關(guān)閉意味這一次會(huì)話的結(jié)束验靡,所以會(huì)話cookie只存在于瀏覽器保持打開(kāi)的狀態(tài)之下吠式。這就是為什么當(dāng)你登錄到一個(gè)web應(yīng)用時(shí)經(jīng)常看到一個(gè)checkbox,詢問(wèn)你是否選擇存儲(chǔ)你的登錄信息:如果你選擇是的話,那么一個(gè)expires選項(xiàng)會(huì)被附加到登錄的cookie中。如果expires選項(xiàng)設(shè)置了一個(gè)過(guò)去的時(shí)間點(diǎn)腹鹉,那么這個(gè)cookie會(huì)被立即刪除。
domain選項(xiàng)(The domain option)
下一個(gè)選項(xiàng)是domain敷硅,指示cookie將要發(fā)送到哪個(gè)域或那些域中功咒。默認(rèn)情況下,domain會(huì)被設(shè)置為創(chuàng)建該cookie的頁(yè)面所在的域名绞蹦。domain選項(xiàng)被用來(lái)擴(kuò)展cookie值所要發(fā)送域的數(shù)量力奋。例如:
path選項(xiàng)(The path option)
另一個(gè)控制何時(shí)發(fā)送Cookie消息頭的方式是指定path選項(xiàng)。與domain選項(xiàng)相同的是幽七,path指明了在發(fā)Cookie消息頭之前必須在請(qǐng)求資源中存在一個(gè)URL路徑景殷。這個(gè)比較是通過(guò)將path屬性值與請(qǐng)求的URL從頭開(kāi)始逐字符串比較完成的。如果字符匹配澡屡,則發(fā)送Cookie消息頭猿挚,例如:
Set-Cookie:name=Nicholas;path=/blog
在這個(gè)例子中,path選項(xiàng)值會(huì)與/blog,/blogrool等等相匹配驶鹉;任何以/blog開(kāi)頭的選項(xiàng)都是合法的。要注意的是只有在domain選項(xiàng)核實(shí)完畢之后才會(huì)對(duì)path屬性進(jìn)行比較室埋。path屬性的默認(rèn)值是發(fā)送Set-Cookie消息頭所對(duì)應(yīng)的URL中的path部分踏兜。
secure選項(xiàng)(The secure option)
最后一個(gè)選項(xiàng)是secure八秃。不像其它選項(xiàng),該選項(xiàng)只是一個(gè)標(biāo)記并且沒(méi)有其它的值肉盹。一個(gè)secure cookie只有當(dāng)請(qǐng)求是通過(guò)SSL和HTTPS創(chuàng)建時(shí),才會(huì)發(fā)送到服務(wù)器端上忍。這種cookie的內(nèi)容意指具有很高的價(jià)值并且可能潛在的被破解以純文本形式傳輸。例如:
Set-Cookie:name=Nicholas;secure
現(xiàn)實(shí)中窍蓝,機(jī)密且敏感的信息絕不應(yīng)該在cookies中存儲(chǔ)或傳輸腋颠,因?yàn)閏ookies的整個(gè)機(jī)制都是原本不安全的。默認(rèn)情況下吓笙,在HTTPS鏈接上傳輸?shù)腸ookies都會(huì)被自動(dòng)添加上secure選項(xiàng)。
session定義
session絮蒿,中文經(jīng)常翻譯為會(huì)話叁鉴,其本來(lái)的含義是指有始有終的一系列動(dòng)作/消息。然而當(dāng)session一詞與網(wǎng)絡(luò)協(xié)議相關(guān)聯(lián)時(shí)但壮,它又往往隱含了“面向連接”和/或“保持狀態(tài)”這樣兩個(gè)含義常侣,“面向連接”指的是在通信雙方在通信之前要先建立一個(gè)通信的渠道⊙椴校“保持狀態(tài)”則是指通信的一方能夠把一系列的消息關(guān)聯(lián)起來(lái)巾乳,使得消息之間可以互相依賴。 而到了web服務(wù)器蓬勃發(fā)展的時(shí)代氨鹏,session在web開(kāi)發(fā)語(yǔ)境下的語(yǔ)義又有了新的擴(kuò)展压状,它的含義是指一類用來(lái)在客戶端與服務(wù)器之間保持狀態(tài)的解決方案跟继。
session機(jī)制
session機(jī)制是一種服務(wù)器端的機(jī)制舔糖,服務(wù)器使用一種類似于散列表的結(jié)構(gòu)(也可能就是使用散列表)來(lái)保存信息莺匠。
當(dāng)程序需要為某個(gè)客戶端的請(qǐng)求創(chuàng)建一個(gè)session的時(shí)候,服務(wù)器首先檢查這個(gè)客戶端的請(qǐng)求里是否已包含了一個(gè)session標(biāo)識(shí) - 稱為session id摇庙,如果已包含一個(gè)session id則說(shuō)明以前已經(jīng)為此客戶端創(chuàng)建過(guò)session遥缕,服務(wù)器就按照session id把這個(gè)session檢索出來(lái)使用(如果檢索不到,可能會(huì)新建一個(gè))夕凝,如果客戶端請(qǐng)求不包含session id封孙,則為此客戶端創(chuàng)建一個(gè)session并且生成一個(gè)與此session相關(guān)聯(lián)的session id,session id的值應(yīng)該是一個(gè)既不會(huì)重復(fù)泡徙,又不容易被找到規(guī)律以仿造的字符串膜蠢,這個(gè)session id將被在本次響應(yīng)中返回給客戶端保存。
保存這個(gè)session id的方式可以采用cookie礁竞,這樣在交互過(guò)程中瀏覽器可以自動(dòng)的按照規(guī)則把這個(gè)標(biāo)識(shí)發(fā)揮給服務(wù)器杉辙。一般這個(gè)cookie的名字都是類似于SEEESIONID蜘矢。這種情況下會(huì)有一個(gè)問(wèn)題,就是當(dāng)cookie被禁用后岖食,無(wú)法傳遞sessionid舞吭。所以會(huì)通過(guò)url傳參數(shù)方式保持會(huì)話析珊。
session存放
session存放在服務(wù)器內(nèi)存中蔑穴,不過(guò)session可以通過(guò)特殊的方式做持久化管理存和。
Android中的Http請(qǐng)求
Android中提供了兩種執(zhí)行網(wǎng)絡(luò)請(qǐng)求的方式,一種是使用apache的HttpClient,另一個(gè)是用java的HttpURLConnection芜茵。
HttpClient
在Android開(kāi)發(fā)中,android sdk附帶了apache的httpclient绞佩,可以使用它的對(duì)象來(lái)執(zhí)行g(shù)et和post調(diào)用猪钮。一般使用步驟如下:
- 使用DefaultHttpClient類實(shí)例化HttpClient對(duì)象烤低;
- 創(chuàng)建HttpGet或HttpPost對(duì)象,將要請(qǐng)求的URL通過(guò)構(gòu)造方法傳入HttpGet或HttpPost對(duì)象涯呻;
- 調(diào)用execute方法發(fā)送Get或Post請(qǐng)求腻要,并返回HttpResponse對(duì)象;
- 通過(guò)HttpResponse接口的getEntity方法返回響應(yīng)信息效诅,并通過(guò)相應(yīng)的處理趟济。
HttpURLConnection
android 2.2版本及之前用HttpClient,而在2.3版本之后篡腌,HttpURLConnection則是最佳選擇勾效。它的壓縮和緩存機(jī)制可以有效地減少網(wǎng)絡(luò)訪問(wèn)的流量,在Android 6.0中杨伙,HttpClient庫(kù)已經(jīng)被移除限匣。
private void sendRequest(String url) throws IOException {
InputStream is = null;
try {
URL newUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) newUrl.openConnection();
// 設(shè)置讀取超時(shí)為10秒
conn.setReadTimeout(10000);
// 設(shè)置鏈接超時(shí)為15秒
conn.setConnectTimeout(15000);
// 設(shè)置請(qǐng)求方式
conn.setRequestMethod("POST");
// 接收輸入流
conn.setDoInput(true);
// 啟動(dòng)輸入流,當(dāng)需要傳遞參數(shù)時(shí)需要開(kāi)啟
conn.setDoOutput(true);
// 添加Header
conn.ssetRequestProperty("Connection", "Keep-Alive");
// 添加請(qǐng)求參數(shù)
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
paramsList.add(new BasicNameValuePair("username", "name"));
paramsList.add(new BasicNameValuePair("pwd", "pwd"));
writeParams(conn,getOutputStream(), paramsList);
conn.connect();
is = conn.getInputStream();
...
} finally {
if (is != null) {
is.close();
}
}
}