一.網絡管理
ConnectivityManager類來監控網絡連接的狀態,,配置錯誤處理等
1.檢查網絡狀態 ConnectivityManager cm =
|
2.檢查網絡類型 boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
|
3.監控網絡狀態 <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
二.HTTP通信
1.Http協議
一、HTTP協議詳解之請求
//請求行POST /reg.jsp HTTP/ (CRLF)
1.User-Agent
1.1什麼是User-Agent
示例
谷歌Android: 蘋果iPhone 4: 蘋果iPad 2: 諾基亞N97: 更多款手機的User-Agent:http://www.zytrax.com/tech/web/mobile_ids.html
|
User-Agent是Http協議中的一部分,屬於頭域的組成部分,User Agent也簡稱UA。用較爲普通的一點來說,是一種向訪問網站提供你所使用的瀏覽器類型、操作系統及版本、CPU 類型、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等信息的標識。UA字符串在每次瀏覽器 HTTP 請求時發送到服務器!
瀏覽器UA 字串的標準格式爲: 瀏覽器標識 (操作系統標識; 加密等級標識; 瀏覽器語言) 渲染引擎標識 版本信息
操作系統標識
FreeBSD
X11; FreeBSD (version no.) i386
X11; FreeBSD (version no.) AMD64
Linux
X11; Linux ppc
X11; Linux ppc64
X11; Linux i686
X11; Linux x86_64
Mac
Macintosh; PPC Mac OS X
Macintosh; Intel Mac OS X
Solaris
X11; SunOS i86pc
X11; SunOS sun4u
Windows:
Windows NT 6.1 對應操作系統 windows 7
Windows NT 6.0 對應操作系統 windows vista
Windows NT 5.2 對應操作系統 windows 2003
Windows NT 5.1 對應操作系統 windows xp
Windows NT 5.0 對應操作系統 windows 2000
Windows ME
Windows 98
加密等級標識
N: 表示無安全加密
I: 表示弱安全加密
U: 表示強安全加密
瀏覽器語言
在首選項 > 常規 > 語言中指定的語言
渲染引擎
瀏覽器 使用 Presto 渲染引擎,格式爲: Presto/版本號
版本信息
顯示 瀏覽器 真實版本信息,格式爲: Version/版本號
1.2、User-Agent的歷史
1993年,NCSA 發佈了首款 web 瀏覽器 Mosaic。它的 user-agent 字串非常簡潔:
Mosaic/0.9雖然當時由於它對操作系統和平臺的依賴性,但是基本格式還是很簡單明瞭。在文本中,斜槓前面是產品名稱(可能會顯示爲 NCSA Mosaic 或是其他類似的字),斜槓後面是產品版本號。
Netscape Communications 開發了 web 瀏覽器 Mozilla(當時號稱“Mosaic 殺手”)。他們首款公開發行版本: Netscape Navigator 2 的user-agent 字串具有如下格式:
Mozilla/Version [Language] (Platform; Encryption)Netscape 按之前的做法在 user-agent 字串的前半部分使用了產品名稱和產品版本,但在後面增加了下列信息:
Language - 表示應用程序用的是哪個語言 Platform - 表示應用程序是在什麼操作系統和/或平臺中運行 Encryption - 表示應用程序包含了什麼安全加密類型。其中的值可能是U(128位加密)、I(40位加密)、N(沒加密)。 Netscape Navigator 2 的user-agent 字串的示例:
Mozilla/2.02 [fr] (WinNT; I)上面的字串指: Netscape Navigator 2.02 、法語 、Windows NT 、40位加密。在當時,通過user-agent 字串中的產品名稱,可以正確判斷使用的是哪個 web 瀏覽器。Netscape Navigator 3 、Internet Explorer 31996年,Netscape Navigator 3 發佈,它遠遠超過 Mosaic 成爲當時最流行的 web 瀏覽器。而user-agent 字串只有些小的變化:去掉了語言部分,多了個放操作系統或CPU的可選信息。格式如下:
Mozilla/Version (Platform; Encryption [; OS-or-CPU description])在 Windows 系統中 Netscape Navigator 3 的user-agent 字串的示例:
Mozilla/3.0 (Win95; U)上面的字串指:Netscape Navigator 3 、Windows 95 、128 位加密。在 Windows 系統中,字串裏面不會顯示 OS 或 CPU 的信息。
Netscape Navigator 3 發佈不久,微軟公佈了它的首款 web 瀏覽器: IE 3 ¹,但是 Netscape 是當時首選瀏覽器,大多數服務器在加載頁面前都會檢查 user-agent 是否爲該款瀏覽器。IE 如果不兼容Netscape user-agent 字串,使用 IE 的用戶就根本打不開這些頁面,於是造就瞭如下格式:
Mozilla/2.0 (compatible; MSIE Version; Operating System)在 Windows 95 中 IE 3.02 的user-agent 字串的示例:
Mozilla/2.0 (compatible; MSIE 3.02; Windows 95)由於當時的瀏覽器嗅探只查 user-agent 字串中的產品名稱部分,結果 IE 搖身一變被識別成了 Mozilla,僞裝成 Netscape Navigator。這個做法引發了對瀏覽器識別的爭論。從此以後,瀏覽器真正的版本埋沒在了字串的中間。Netscape Communicator 4 、Internet Explorer 4至81997年8月,Netscape Communicator 4 發佈(發佈的名稱中 Navigator 換成了 Communicator),它的 user-agent 字串格式與 3 版本一致。Windows 98 中 4 版本的user-agent 字串如下:
Mozilla/4.0 (Win98; I)Netscape 瀏覽器在更新時,版本也相應增加。4.79 版本的 user-agent 字串如下:
Mozilla/4.79 (Win98; I)微軟發佈 IE 4 時,user-agent 字串更新了版本,格式如下:
Mozilla/4.0 (compatible; MSIE Version; Operating System)在 Windows 98 中 IE 4 的user-agent 字串的示例:
Mozilla/4.0 (compatible; MSIE 4.0; Windows 98)可以看出,Mozilla 的版本與 IE 實際的版本一致,這樣就可以識別第4代瀏覽器了。但遺憾的是,不久 IE 4.5 馬上就發佈了(只在 Mac 平臺),雖然 Mozilla 版本仍是 4,但是 IE 的版本改成如下:
Mozilla/4.0 (compatible; MSIE 4.5; Mac_PowerPC)此後,IE 的版本一直到 7 都沿用了這個模式。
而 IE 8 的 user-agent 字串添加了呈現引擎(rendering engine)版本:
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)新增的呈現引擎非常重要!這樣 IE8 以 MSIE 7.0 兼容模式運行時,Trident 版本保持不變,而原先 IE7 的user-agent 字串不包括 Trident 版本。這樣可以區分 IE7 與 IE8 運行的兼容模式。
注意:別指望能從 Mozilla 版本中得到什麼靠譜的信息。
Gecko 是 Firefox 的呈現引擎。Gecko 首次開發是作爲 Mozilla 瀏覽器 Netscape 6 的一部分。Netscape 6 的user-agent 字串的結構是面向未來的,新版本反應出從 4.x 版本的簡單變得較爲複雜,它的格式如下:
Mozilla/MozillaVersion (Platform; Encryption; OS-or-CPU; Language; PrereleaseVersion)Gecko/GeckoVersion ApplicationProduct/ApplicationProductVersion爲了更好的理解上面的 Geckouser-agent 字串格式,下面來看看各種從基於 Gecko 瀏覽器中取得的字串。
在 Windows XP 中的 Netscape 6.21:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128 Netscape6/6.2.1在 Linux 中的 SeaMonkey 1.1a:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1b2) Gecko/20060823 SeaMonkey/1.1a在 Windows XP 中的 Firefox 2.0.0.11 :
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11Mac OS X 中的 Camino 1.5.1:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.6) Gecko/20070809 Camino/1.5.1上面都是基於 Gecko 的瀏覽器所取得的user-agent 字串,區別只是版本有所不同。Mozilla 版本 5.0 是自從首款基於 Gecko 發佈後就一直不變,而且以後有可能也不會變²。
2003年,Apple 宣佈發佈首款他們自主開發的 web 瀏覽器:Safari。它的呈現引擎叫 WebKit。它是 Linux 中的 web 瀏覽器 Konqueror 呈現引擎 KHTML 的一個分支,幾年後,WebKit 的開源吸引了呈現引擎的開發人員。
這款新瀏覽器和呈現引擎的開發人員也遇到了曾經 IE 3.0 類似的問題:怎樣才能溶入主流而不被踢出局?答案是:在 user-agent 字串中放詳盡的信息,以便騙取網站的信任使它與其它流行的瀏覽器兼容。user-agent字串格式如下:
Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Safari/SafariVersion下面是示例:
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1這又是個挺長的user-agent 字串,其中包括的信息既有 Apple WebKit 的版本,也有 Safari 的版本。凡是基於 WebKit 的瀏覽器都將自己僞裝成了 Mozilla 5.0,與基於 Gecko 瀏覽器完全一樣。但 Safari 的版本是瀏覽器的構建版本號(build number)。Safari 1.25 在user-agent 字串中號爲 125.1(如上所示)。Safari 版本 3 的user-agent 字串包括了實際的 Safari 版本:
Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5其中的“(KHTML, like Gecko)”在 Safari 1.0 預覽版本中就有了,這字串部分是最耐人尋味又飽受詬病。Apple 的野心是爲了讓開發人員把 Safari 當成 Gecko,所以採取了當初微軟 IEuser-agent 的類似做法:Safari 是兼容 Mozilla 的,否則 Safari 用戶會認爲用的瀏覽器不受支持。
而其它基於 WebKit 的瀏覽器與 Safari 不同的是,沒有上面說的這個情況,所以檢測斷定瀏覽器是否基於 WebKit 比看有沒有明確標 Safari 更有用。
Konqueror 是款在 KDE Linux 桌面環境中的瀏覽器,基於 KHTML 開源呈現引擎。它只發布了在 Linux 的版本,但是擁有活躍的用戶羣。爲了兼容性最大化,user-agent 字串的格式也緊跟 IE 的後塵:
Mozilla/5.0 (compatible; Konqueror/Version; OS-or-CPU)Konqueror 3.2 爲了與 WebKituser-agent 字串變化保持一致,它將 KHTML 作爲它的標識:
Mozilla/5.0 (compatible; Konqueror/Version; OS-or-CPU) KHTML/KHTMLVersion (like Gecko)如下所示:
Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko)Konqueror 和 KHTML 的版本號比較一致,唯一的區別就是下點處不同,比如Konquerer 3.5、KHTML 3.5.1。
Google Chrome 瀏覽器以 WebKit 作爲呈現引擎,JavaScript 引擎卻用了另一種。最初發布的版本是 0.2,它的 user-agent 字串格式是在 webKit 信息的基礎上又增加了如下:
Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Chrome/ChromeVersion Safari/SafariVersionChrome 0.2user-agent 信息的示例如下:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13雖我不敢完全保證,但很可能 WebKit 版本和 Safari 版本總會保持同步。
Opera 瀏覽器默認 user-agent 字串是現代瀏覽器中最合理的--正確的標識了它自己及其版本。 在 Opera 8.0 前,它的user-agent 字串格式如下:
Opera/Version (OS-or-CPU; Encryption) [Language]在 Windows XP 中 Opera 7.54 user-agent 字串示例:
Opera/7.54 (Windows NT 5.1; U) [en]Opera 8 user-agent 字串的語言部分移到了括號內。
Opera/Version (OS-or-CPU; Encryption; Language)在 Windows XP 中 Opera 8 user-agent 字串示例:
Opera/8.0 (Windows NT 5.1; U; en)當時 Opera 做爲主流瀏覽器之一,它的 user-agent 字串是唯一使用產品名稱和版本完全真實的標識了它自己。但是由於大量的瀏覽器嗅探代碼在 Internet 上像蝗蟲飛過般只吃標 Mozilla 產品名的user-agent 字串,造成了 Opera 的user-agent 字串發生了完全的改變。
Opera 9 user-agent 字串有兩種修改的方式:一種方式是將自己標識爲 Firefox 或 IE 瀏覽器。在這種方式下,user-agent 字串與 Firefox 或 IE 的幾乎一樣,只不過末尾附加了“Opera”及版本號。如下所示:
Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50前一字串將 Opera 9.5 標識爲 Firefox 2。後一字串將 Opera 9.5 標識爲 IE 6,在兩個字串中都帶有 Opera 版本信息。雖然這種方式是作爲 Firefox 或 IE 打開的,但也能識別出 Opera。另一種方法則是瀏覽器 user-agent 字串標識僞裝成 Firefox 或 IE,同時也找不到“Opera”字串及其版本信息。這樣從字面上去區分 Opera 瀏覽器便成了“不可能完成的任務”。
user-agent 字串史可以說明曾對 user-agent 嗅探說不的原因:IE 想要將自己識別爲 Netscape 4,Konqueror 和 WebKit 想要識別爲 Firefox,Chrome 想要識別爲 Safari。這樣使得除 Opera 外所有瀏覽器的user-agent 嗅探區別很小,想要從一堆茫茫瀏覽器海洋中找出有用的標識太少了。關於嗅探要記住:一款瀏覽器與其它瀏覽器是兼容的,這樣造成了不能完全準確的斷定是哪款瀏覽器。
比如說 Chrome ,它聲稱任何可以在 Safari 3 訪問的網站 Chrome 也都可以訪問,但是對檢測 Chrome 沒有一點用。爲了瀏覽器的兼容--這便是這個聲明的理由。
1.3、user-agent的作用
根據前面介紹的user-agent的歷史我們知道,通過user-agent不能完全準確的判斷是屬於那款瀏覽器。由於UA字符串在每次瀏覽器HTTP 請求時發送到服務器,所以服務器就可以根據它來做好多事。
比如:
1、統計用戶瀏覽器使用情況。有些瀏覽器說被多少人使用了,實際上就可以通過判斷每個IP的UA來確定這個IP是用什麼瀏覽器訪問的,以得到使用量的數據。
2、根據用戶使用瀏覽器的不同,顯示不同的排版從而爲用戶提供更好的體驗。有些網站會根據這個來調整打開網站的類型,如是手機的就打開wap,顯示非手機的就打開pc常規頁面。用手機訪問谷歌和電腦訪問是不一樣的,這些是谷歌根據訪問者的UA來判斷的。
既然知道了UA的作用,那麼其實客戶端也可以使用UA來做一些神奇的事。
比如:僞裝 user agent 來回避某些偵測特定瀏覽器才能讀取的網站。
如果使用Firefox瀏覽器插件User agent switcher,用戶就可以輕鬆地在不同UA之間切換,把自己僞裝成其他瀏覽器。這樣就可以在PC上預覽WAP或移動格式的網頁,比如專門爲iPhone設計的頁面。
1.4、獲得user-agent的值
既然已經知道user-agent是http的頭域,那我們在編程的時候就可以獲得它。
在 ASP.NET 中使用 Request.Header["User-Agent"] 得到瀏覽器的 User Agent,也可以使用 Request.UserAgent 來獲取;
Java 中使用 request.getHeader(”User-Agent”) 來獲得;
PHP 中相應使用:$_SERVER[HTTP_USER_AGENT];
JS中則使用navigator.userAgent來獲得(是否記得在客戶端經常使用它來做瀏覽器兼容呢)。
//消息報頭Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/*(CRLF)
Accept-Language:zh-cn (CRLF)
Accept-Encoding:gzip,deflate (CRLF)
If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
Host:www.guet.edu.cn (CRLF)
Connection:Keep-Alive (CRLF)
(CRLF)
//請求正文
user=jeffrey&pwd=1234
以上是http請求的三部:請求行、消息報頭、請求正文。
請求行以一個方法符號開頭,以空格分開,後面跟着請求的URI和協議的版本,格式如下:
Method Request-URI HTTP-Version CRLF
其中 Method表示請求方法(如POST、GET、PUT、DELETE等);Request-URI是一個統一資源標識符;HTTP-Version表示請求的HTTP協議版本;CRLF表示回車和換行。
二、HTTP協議詳解之響應篇
//狀態行HTTP/1.1 200 OK (CRLF)
//消息報頭Cache-Control: private, max-age=30
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Expires: Mon, 25 May 2009 03:20:33 GMT
Last-Modified: Mon, 25 May 2009 03:20:03 GMT
Vary: Accept-Encoding
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Mon, 25 May 2009 03:20:02 GMT
Content-Length: 12173
//響應正文略
HTTP響應也是由三個部分組成,分別是:狀態行、消息報頭、響應正文
狀態行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
其中,HTTP-Version表示服務器HTTP協議的版本;Status-Code表示服務器發回的響應狀態代碼;Reason-Phrase表示狀態代碼的文本描述。
常見狀態代碼、狀態描述、說明:
200 OK //客戶端請求成功
400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用
403 Forbidden //服務器收到請求,但是拒絕提供服務
404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發生不可預期的錯誤
503 Server Unavailable //服務器當前不能處理客戶端的請求,一段時間後可能恢復正常
三、HTTP協議詳解之消息報頭
HTTP消息由客戶端到服務器的請求和服務器到客戶端的響應組成。請求消息和響應消息都是由開始行(對於請求消息,開始行就是請求行;對於響應消息,開始行就是狀態行),消息報頭(可選),空行(只有CRLF的行),消息正文(可選)組成。
HTTP消息報頭包括普通報頭、請求報頭、響應報頭、實體報頭。每一個報頭域都是由名字+“:”+空格+值 組成,消息報頭域的名字是大小寫無關的。
1、請求報頭
請求報頭允許客戶端向服務器端傳遞請求的附加信息以及客戶端自身的信息。
常用的請求報頭
Accept請求報頭域用於指定客戶端接受哪些類型的信息。
Accept-Charset請求報頭域用於指定客戶端接受的字符集。
Accept-Encoding請求報頭域類似於Accept,但是它是用於指定可接受的內容編碼。
Accept-Language請求報頭域類似於Accept,但是它是用於指定一種自然語言。
Authorization請求報頭域主要用於證明客戶端有權查看某個資源。
Host請求報頭域主要用於指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的。User-Agent請求報頭域允許客戶端將它的操作系統、瀏覽器和其它屬性告訴服務器。
2、響應報頭
響應報頭允許服務器傳遞不能放在狀態行中的附加響應信息,以及關於服務器的信息和對Request-URI所標識的資源進行下一步訪問的信息。
常用的響應報頭
Location響應報頭域用於重定向接受者到一個新的位置。Location響應報頭域常用在更換域名的時候。
Server響應報頭域包含了服務器用來處理請求的軟件信息
3. 實體報頭
請求和響應消息都可以傳送一個實體。
常用的實體報頭
Content-Encoding指示已經被應用到實體正文的附加內容的編碼。
Content-Language實體報頭域描述了資源所用的自然語言。
Content-Length實體報頭域用於指明實體正文的長度,以字節方式存儲的十進制數字來表示。
Content-Type實體報頭域用語指明發送給接收者的實體正文的媒體類型。
Last-Modified實體報頭域用於指示資源的最後修改日期和時間。
Expires實體報頭域給出響應過期的日期和時間。
四、補充
1、HTTP協議Content Lenth限制漏洞導致拒絕服務攻擊
使用POST方法時,可以設置ContentLenth來定義需要傳送的數據長度,例如ContentLenth:999999999,在傳送完成前,內 存不會釋放,攻擊者可以利用這個缺陷,連續向WEB服務器發送垃圾數據直至WEB服務器內存耗盡。這種攻擊方法基本不會留下痕跡。
2、爲了提高用戶使用瀏覽器時的性能,現代瀏覽器還支持併發的訪問方式,瀏覽一個網頁時同時建立多個連接,以迅速獲得一個網頁上的多個圖標,這樣能更快速完成整個網頁的傳輸。HTTP1.1中提供了這種持續連接的方式,而下一代HTTP協議:HTTP-NG更增加了有關會話控制、豐富的內容協商等方式的支持,來提供更高效率的連接。
五.Java利用HTTP協議實現聯網和下載
Url的請求連接(Get方式)
String currentUrl=“http://www.myWeb.com/login.jsp?userName='Devin'&passWord='mypassword'”; //URL ?後面的內容爲HTTP請求的正文
URL url = new URL(currentUrl);
HttpURLConnection httpurlconnection = url.openConnection();
//下面的設置對應HTTP請求中的消息報頭
httpurlconnection.setRequestProperty("User-Agent",CommonValues.User_Agent);
httpurlconnection.setRequestProperty("Accept",CommonValues.Accept);
httpurlconnection.setRequestProperty("Accept-Charset",CommonValues.Accept_Charset);
httpurlconnection.setRequestProperty("Accept-Language",CommonValues.Accept_Language);
httpurlconnection.setRequestProperty("Connection",CommonValues.Connection);
httpurlconnection.setRequestProperty("Keep-Alive",CommonValues.Keep_Alive);
httpurlconnection.setConnectTimeout(CommonValues.ConnectionTimeOut);
httpurlconnection.setReadTimeout(CommonValues.ReadTimeOut);
httpurlconnection.connect();
int responsecode = httpurlconnection.getResponseCode();
if(responsecode == HttpURLConnection.HTTP_OK) //對應HTTP響應中狀態行的響應碼
{
//操作請求流,這裏對應HTTP響應中的響應正文
}
if (httpurlconnection != null)
{
httpurlconnection.disconnect();
}
HTTP請求:GET與POST方法的區別
HTTP 定義了與服務器交互的不同方法,最基本的方法是 GET 和 POST。
1.Get是從服務器上獲取數據,Post是向服務器傳送數據。GET 用於信息獲取,是安全的和冪等的。安全的意味着該操作用於獲取信息而非修改信息,冪等的意味着對同一 URL 的多個請求應該返回同樣的結果。完整的定義並不像看起來那樣嚴格。
2.GET請求請提交的數據放置在HTTP請求協議頭中,附加在url之後,以?分開與url分開;而POST提交的數據則放在實體數據中,即在HTML HEADER內提交。
3.GET方式提交的數據最多只能有1024字節,而POST則沒有此限制。
4.安全性問題。使用 Get 的時候,參數會顯示在地址欄上,而 Post 不會。所以,如果這些數據是中文數據而且是非敏感數據,那麼使用 get;如果用戶輸入的數據不是中文字符而且包含敏感數據,那麼還是使用 post爲好。
5、Get是Form的默認方法。
Android網絡連接之HttpURLConnection和HttpClient
1.概念
HTTP 協議可能是現在 Internet 上使用得最多、最重要的協議了,越來越多的 Java 應用程序需要直接通過 HTTP 協議來訪問網絡資源。在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能:HttpURLConnection。但是對於大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。
除此之外,在Android中,androidSDK中集成了Apache的HttpClient模塊,用來提供高效的、最新的、功能豐富的支持 HTTP 協議工具包,並且它支持 HTTP 協議最新的版本和建議。使用HttpClient可以快速開發出功能強大的Http程序。
2.區別
HttpClient是個很不錯的開源框架,封裝了訪問http的請求頭,參數,內容體,響應等等,
HttpURLConnection是java的標準類,什麼都沒封裝,用起來太原始,不方便,比如重訪問的自定義,以及一些高級功能等。
|
URLConnection |
HTTPClient |
Proxies and SOCKS |
Full support in Netscape browser, appletviewer, and applications (SOCKS: Version 4 only); no additional limitations from security policies. |
Full support (SOCKS: Version 4 and 5); limited in applets however by security policies; in Netscape can't pick up the settings from the browser. |
Authorization |
Full support for Basic Authorization in Netscape (can use info given by the user for normal accesses outside of the applet); no support in appletviewer or applications. |
Full support everywhere; however cannot access previously given info from Netscape, thereby possibly requesting the user to enter info (s)he has already given for a previous access. Also, you can add/implement additional authentication mechanisms yourself. |
Methods |
Only has GET and POST. |
Has HEAD, GET, POST, PUT, DELETE, TRACE and OPTIONS, plus any arbitrary method. |
Headers |
Currently you can only set any request headers if you are doing a POST under Netscape; for GETs and the JDK you can't set any headers. Under Netscape 3.0 you can read headers only if the resource was returned with a Content-length header; if no Content-length header was returned, or under previous versions of Netscape, or using the JDK no headers can be read. |
Allows any arbitrary headers to be sent and received. |
Automatic Redirection Handling |
Yes. |
Yes (as allowed by the HTTP/1.1 spec). |
Persistent Connections |
No support currently in JDK; under Netscape uses HTTP/1.0 Keep-Alive's. |
Supports HTTP/1.0 Keep-Alive's and HTTP/1.1 persistence. |
Pipelining of Requests |
No. |
Yes. |
Can handle protocols other than HTTP |
Theoretically; however only http is currently implemented. |
No. |
Can do HTTP over SSL (https) |
Under Netscape, yes. Using Appletviewer or in an application, no. |
No (not yet). |
Source code available |
No. |
Yes. |
3.案例
URLConnection
String urlAddress = "http://192.168.1.102:8080/AndroidServer/login.do";
URL url;
HttpURLConnection uRLConnection;
public UrlConnectionToServer(){
}
//向服務器發送get請求
public String doGet(String username,String password){
String getUrl = urlAddress + "?username="+username+"&password="+password;
try {
url = new URL(getUrl);
uRLConnection = (HttpURLConnection)url.openConnection();
InputStream is = uRLConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String response = "";
String readLine = null;
while((readLine =br.readLine()) != null){
//response = br.readLine(); response = response + readLine;
}
is.close();
br.close();
uRLConnection.disconnect();
return response;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
//向服務器發送post請求
public String doPost(String username,String password){
try {
url = new URL(urlAddress);
uRLConnection = (HttpURLConnection)url.openConnection();
uRLConnection.setDoInput(true);
uRLConnection.setDoOutput(true);
uRLConnection.setRequestMethod("POST");
uRLConnection.setUseCaches(false);
uRLConnection.setInstanceFollowRedirects(false);
uRLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
uRLConnection.connect();
DataOutputStream out = new DataOutputStream(uRLConnection.getOutputStream());
String content = "username="+username+"&password="+password;
out.writeBytes(content);
out.flush();
out.close();
InputStream is = uRLConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String response = "";
String readLine = null;
while((readLine =br.readLine()) != null){
//response = br.readLine(); response = response + readLine;
}
is.close();
br.close();
uRLConnection.disconnect();
return response;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
HTTPClient
String urlAddress = "http://192.168.1.102:8080/qualityserver/login.do";
public HttpClientServer(){
}
public String doGet(String username,String password){
String getUrl = urlAddress + "?username="+username+"&password="+password;
HttpGet httpGet = new HttpGet(getUrl);
HttpParams hp = httpGet.getParams();
hp.getParameter("true");
//hp.
//httpGet.setp HttpClient hc = new DefaultHttpClient();
try {
HttpResponse ht = hc.execute(httpGet);
if(ht.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
HttpEntity he = ht.getEntity();
InputStream is = he.getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String response = "";
String readLine = null;
while((readLine =br.readLine()) != null){
//response = br.readLine(); response = response + readLine;
}
is.close();
br.close();
//String str = EntityUtils.toString(he); System.out.println("========="+response);
return response;
}else{
return "error";
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block e.printStackTrace();
return "exception";
} catch (IOException e) {
// TODO Auto-generated catch block e.printStackTrace();
return "exception";
}
}
public String doPost(String username,String password){
//String getUrl = urlAddress + "?username="+username+"&password="+password; HttpPost httpPost = new HttpPost(urlAddress);
List params = new ArrayList();
NameValuePair pair1 = new BasicNameValuePair("username", username);
NameValuePair pair2 = new BasicNameValuePair("password", password);
params.add(pair1);
params.add(pair2);
HttpEntity he;
try {
he = new UrlEncodedFormEntity(params, "gbk");
httpPost.setEntity(he);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block e1.printStackTrace();
}
HttpClient hc = new DefaultHttpClient();
try {
HttpResponse ht = hc.execute(httpPost);
//連接成功 if(ht.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
HttpEntity het = ht.getEntity();
InputStream is = het.getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String response = "";
String readLine = null;
while((readLine =br.readLine()) != null){
//response = br.readLine(); response = response + readLine;
}
is.close();
br.close();
//String str = EntityUtils.toString(he); System.out.println("=========&&"+response);
return response;
}else{
return "error";
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block e.printStackTrace();
return "exception";
} catch (IOException e) {
// TODO Auto-generated catch block e.printStackTrace();
return "exception";
}
}
servlet端json轉化:
resp.setContentType("text/json");
resp.setCharacterEncoding("UTF-8");
toDo = new ToDo();
List<UserBean> list = new ArrayList<UserBean>();
list = toDo.queryUsers(mySession);
String body;
//設定JSON JSONArray array = new JSONArray();
for(UserBean bean : list)
{
JSONObject obj = new JSONObject();
try
{
obj.put("username", bean.getUserName());
obj.put("password", bean.getPassWord());
}catch(Exception e){}
array.add(obj);
}
pw.write(array.toString());
System.out.println(array.toString());
android端接收:
String urlAddress = "http://192.168.1.102:8080/qualityserver/result.do";
String body =
getContent(urlAddress);
JSONArray array = new JSONArray(body);
for(int i=0;i<array.length();i++)
{
obj = array.getJSONObject(i);
sb.append("用戶名:").append(obj.getString("username")).append("\t");
sb.append("密碼:").append(obj.getString("password")).append("\n");
HashMap<String, Object> map = new HashMap<String, Object>();
try {
userName = obj.getString("username");
passWord = obj.getString("password");
} catch (JSONException e) {
e.printStackTrace();
}
map.put("username", userName);
map.put("password", passWord);
listItem.add(map);
}
} catch (Exception e) {
// TODO Auto-generated catch block e.printStackTrace();
}
if(sb!=null)
{
showResult.setText("用戶名和密碼信息:");
showResult.setTextSize(20);
} else
extracted();
//設置adapter SimpleAdapter simple = new SimpleAdapter(this,listItem,
android.R.layout.simple_list_item_2,
new String[]{"username","password"},
new int[]{android.R.id.text1,android.R.id.text2});
listResult.setAdapter(simple);
listResult.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
int positionId = (int) (id+1);
Toast.makeText(MainActivity.this, "ID:"+positionId, Toast.LENGTH_LONG).show();
}
});
}
private void extracted() {
showResult.setText("沒有有效的數據!");
}
//和服務器連接 private String getContent(String url)throws Exception{
StringBuilder sb = new StringBuilder();
HttpClient client =new DefaultHttpClient();
HttpParams httpParams =client.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 3000);
HttpConnectionParams.setSoTimeout(httpParams, 5000);
HttpResponse response = client.execute(new HttpGet(url));
HttpEntity entity =response.getEntity();
if(entity !=null){
BufferedReader reader = new BufferedReader(new InputStreamReader
(entity.getContent(),"UTF-8"),8192);
String line =null;
while ((line= reader.readLine())!=null){
sb.append(line +"\n");
}
reader.close();
}
return sb.toString();
}
android之HttpURLConnection
1.HttpURLConnection連接URL1)創建一個URL對象
URL url = new URL(http://www.baidu.com/);
2)利用HttpURLConnection對象從網絡中獲取網頁數據
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
3)設置連接超時
conn.setConnectTimeout(6*1000);
4)對響應碼進行判斷
if (conn.getResponseCode() != 200) //從Internet獲取網頁,發送請求,將網頁以流的形式讀回來
throw new RuntimeException("請求url失敗");
5)得到網絡返回的輸入流
InputStream is = conn.getInputStream();
6)String result = readData(is, "GBK"); //文件流輸入出文件用outStream.write
7)conn.disconnect();
總結:--記得設置連接超時,如果網絡不好,Android系統在超過默認時間會收回資源中斷操作.
--返回的響應碼200,是成功.
--在Android中對文件流的操作和JAVA SE上面是一樣的.
--在對大文件的操作時,要將文件寫到SDCard上面,不要直接寫到手機內存上.
--操作大文件是,要一遍從網絡上讀,一遍要往SDCard上面寫,減少手機內存的使用.這點很重要,面試經常會被問到.
--對文件流操作完,要記得及時關閉.
2.向Internet發送請求參數
步驟:
1)創建URL對象:URL realUrl = new URL(requestUrl);
2)通過HttpURLConnection對象,向網絡地址發送請求
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
3)設置容許輸出:conn.setDoOutput(true);
4)設置不使用緩存:conn.setUseCaches(false);
5)設置使用POST的方式發送:conn.setRequestMethod("POST");
6)設置維持長連接:conn.setRequestProperty("Connection", "Keep-Alive");
7)設置文件字符集:conn.setRequestProperty("Charset", "UTF-8");
8)設置文件長度:conn.setRequestProperty("Content-Length", String.valueOf(data.length));
9)設置文件類型:conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
10)以流的方式輸出.
總結:--發送POST請求必須設置允許輸出
--不要使用緩存,容易出現問題.
--在開始用HttpURLConnection對象的setRequestProperty()設置,就是生成HTML文件頭.
3.向Internet發送xml數據XML格式是通信的標準語言,Android系統也可以通過發送XML文件傳輸數據.
1)將生成的XML文件寫入到byte數組中,並設置爲UTF-8:byte[] xmlbyte = xml.toString().getBytes("UTF-8");
2)創建URL對象,並指定地址和參數:URL url = new URL(http://localhost:8080/itcast/contanctmanage.do?method=readxml);
3)獲得鏈接:HttpURLConnection conn = (HttpURLConnection) url.openConnection();
4)設置連接超時:conn.setConnectTimeout(6* 1000);
5)設置允許輸出conn.setDoOutput(true);
6)設置不使用緩存:conn.setUseCaches(false);
7)設置以POST方式傳輸:conn.setRequestMethod("POST");
8)維持長連接:conn.setRequestProperty("Connection", "Keep-Alive");
9)設置字符集:conn.setRequestProperty("Charset", "UTF-8");
10)設置文件的總長度:conn.setRequestProperty("Content-Length", String.valueOf(xmlbyte.length));
11)設置文件類型:conn.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
12)以文件流的方式發送xml數據:outStream.write(xmlbyte);
總結:
--我們使用的是用HTML的方式傳輸文件,這個方式只能傳輸一般在5M一下的文件.
--傳輸大文件不適合用HTML的方式,傳輸大文件我們要面向Socket編程.確保程序的穩定性--將地址和參數存到byte數組中:byte[] data = params.toString().getBytes();
(一)Java接口
主要類:
URL:資源定位符類 |
URLConnection:資源連接 |
HttpURLConnectiion:繼承自URLConnection |
// 設置是否向httpUrlConnection輸出,因爲這個是post請求,參數要放在 // http正文內,因此需要設爲true, 默認情況下是false; httpUrlConnection.setDoOutput(true);
// 設置是否從httpUrlConnection讀入,默認情況下是true; httpUrlConnection.setDoInput(true);
// Post 請求不能使用緩存 httpUrlConnection.setUseCaches(false);
// 設定傳送的內容類型是可序列化的java對象 // (如果不設此項,在傳送序列化對象時,當WEB服務默認的不是這種類型時可能拋java.io.EOFException) httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
// 設定請求的方法爲"POST",默認是GET httpUrlConnection.setRequestMethod("POST");
// 連接,從上述第2條中url.openConnection()至此的配置必須要在connect之前完成, httpUrlConnection.connect(); |
注: a:) HttpURLConnection的connect()函數,實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。 無論是post還是get,http請求實際上直到HttpURLConnection的getInputStream()這個函數裏面才正式發送出去。
|
一般下載器
URL url = new URL(urlname); HttpURLConnection con =(HttpURLConnection) url.openConnection(); con.setDoOutput(true); con.setDoInput(true); con.setUseCaches(false); con.setReadTimeout(2*60*1000); //con.setRequestProperty("Content-type", "application/x-java-serialized-object"); con.connect(); InputStream is = con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is)); StringBuffer sb = new StringBuffer(); String content ; while((content=br.readLine())!=null){ //content = new String(content.getBytes(),"gbk"); sb.append(content); } |
(二)Apache接口
HttpClient:Http客戶端類(抽象類),可有DefaultHttpClient獲得 |
HttpGet/HttpPost類:實現抓取網頁的信息 |
Execute():開始抓取網頁 |
HttpResponse類的getEntity()方法可獲取網頁內容 |
關於Post抓取網頁
需設置以下內容----設置字符集----設置實體
private void initHttpClient() {
HttpGet get = new HttpGet(httpUrl);//抓取http服務器上的信息
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(get);//執行抓取網頁信息,返回一個HttpResponse類的對象一測試是否抓取成功
if((response.getStatusLine().getStatusCode())==HttpStatus.SC_OK){
//抓取成功,可有抓取網頁成功後返回的HttpResponse對象獲取網頁內容
result = EntityUtils.toString(response.getEntity());//獲取網頁內容,返回的是Entity(實體類)類,
2.Android http請求設置網絡超時
HttpParams params = new BasicHttpParams();
HttpConnectionParams . setConnectionTimeout ( params , 3000 );
HttpConnectionParams . setSoTimeout ( params , 3000 );
HttpClient client = new DefaultHttpClient(params);
3.實例代碼
String weatherUrl = "http://113.108.239.107/data/"+cityCode+".html"; //設置超時 HttpParams params =new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 2*60*1000); HttpConnectionParams.setSoTimeout(params, 2*60*1000); HttpGet get = new HttpGet(weatherUrl); HttpResponse response=null; HttpClient client = new DefaultHttpClient(params); try{ response = client.execute(get); }catch(Exception e){ //出現連接超時等意外網絡情況則繼續5次 Log.e(tag, e.toString()); } if(response!=null&&response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){ try { content = EntityUtils.toString(response.getEntity()); JSONParser(content); } catch (ParseException e) { // TODO Auto-generated catch block Log.e(tag, e.toString()); }finally{ } } else{ return false; } return true; } |
二、Socket通信
(一)Android接口
封裝了Http並有Socket---Wifi----Uri
主要類
(1).Socket----Wifi------Uri
(2).ExecutorService:
1.Socket
ServerSocket:服務器端 ServerSocket server ; public void initServer() throws IOException{ server = new ServerSocket(1034);//服務器端的特定端口(Port>1023) Socket s = server.accept();//等待發出請求的客戶端的socket //獲取客戶的請求,即客戶所輸出的輸出流 OutputStream os = s.getOutputStream(); //變爲數據輸出流,篩選 DataOutputStream dos = new DataOutputStream(os); dos.writeUTF("服務端已經收到消息");//向客戶端寫消息 注:此處只要獲取了客戶端的請求,再將輸出流變爲一個可寫流,調用write可向客戶端寫消息 //接受客戶端信息 InputStream is = s.getInputStream(); BufferedReader dis = new BufferedReader(new InputStreamReader(is)); //注:要接收客戶端消息必須要獲得服務端Socket的輸入流,相當於要在服務器端讀取客戶端的消息 server.close();//關閉服務器 |
總結:服務器端要向客戶端接收,發送消息,必須與獲得的Socket對象建立起一個通信通道:即InputStream和OutputStream/可如此理解服務器要寫,所以要獲取客戶端的可寫入流,服務器要讀則要獲取客戶端的可讀入流,其中的流可理解爲一個通道 |
Socket:客戶端 //向服務器請求並讀取服務器信息 Socket s = new Socket("118.124.6.164", 1034);//建立連接服務器的客戶端 //BufferedReader os = new BufferedReader(new InputStreamReader(s.getInputStream()));//獲取服務器信息 //result = os.readLine(); //showText.setText(result); //向服務器寫信息 DataOutputStream dos = new DataOutputStream(s.getOutputStream()); dos.writeChars("client send message"); //PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true); s.close();
|
注:和服務器端一樣,要向服務器寫則需獲取客戶端Socket的可寫入流。要讀取服務器的消息則需要獲取客戶端Socket的可讀入流 |
注:客戶端和服務器端的輸入輸出要對應才行 |
2.實現多線程
ExecutorService:創建一個線程池
(1)獲取線程池
由Executors類的newFixedThreadPool(2)可創建一個指定容量的線程池
newCachedThreadPool()創建一個緩衝線程池
(2)執行線程:execute(Runnable)參數爲一個Runnable對象
四.設置代理
1.HttpUrlConnetion
//當我們使用的是中國移動的手機網絡時,下面方法可以直接獲取得到10.0.0.172,80端口 String host=android.net.Proxy.getDefaultHost();//通過andorid.net.Proxy可以獲取默認的代理地址 int port =android.net.Proxy.getDefaultPort();//通過andorid.net.Proxy可以獲取默認的代理端口 SocketAddress sa=new InetSocketAddress(host,port); //定義代理,此處的Proxy是源自java.net Proxy proxy=new Proxy(java.net.Proxy.Type.HTTP,sa); URL getUrl = new URL(“www.baidu.com”); HttpURLConnection con = (HttpURLConnection) getUrl.openConnection(proxy);//設置代理 |
2.HttpClient
DefaultHttpClient httpClient=new DefaultHttpClient(); String host=Proxy.getDefaultHost();//此處Proxy源自android.net int port = Proxy.getPort(context);//同上 HttpHost httpHost = new HttpHost(host, port); //設置代理 httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY,httpHost); HttpGet httpGet=new HttpPost("<a href="http://www.baidu.com">www.baidu.com</a>"); HttpResponse response=httpClient.execute(httpGet); |
3.實例
DefaultHttpClient httpClient=new DefaultHttpClient();
//網絡檢測
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if(cm!=null){
NetworkInfo ni = cm.getActiveNetworkInfo();
if(ni!=null){
if(!ni.getTypeName().equals("WIFI")){
//設置代理
String host=Proxy.getDefaultHost();
int port = Proxy.getPort(context);
HttpHost httpHost = new HttpHost(host, port);
httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY,httpHost);
}
}
}
五.常見問題
1.二級域名無法訪問
將域名換成ip
三.非阻塞IO
四.網絡編程前的準備
1.使用Chrome瀏覽器的Http頭分析插件分析Http的Request和Response信息
Status |
HTTP/1.1 200 OK |
||
RequestAccept |
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 |
||
Accept-Encoding |
gzip,deflate,sdch |
||
Accept-Language |
zh-CN,zh;q=0.8,en;q=0.6 |
||
Cookie |
lzstat_uv=32461900962640612022|3266776@2993663; |
||
Referer |
http://www.suse.edu.cn/ |
||
User-Agent |
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36 |
||
ResponseCache-Control |
no-cache |
||
Content-Encoding |
gzip |
||
Content-Length |
10911 |
||
Content-Type |
text/html; charset=utf-8 |
||
Date |
Thu, 27 Mar 2014 12:16:47 GMT |
||
Expires |
-1 |
||
Pragma |
no-cache,no-cache |
||
Server |
Microsoft-IIS/7.5 |
||
Vary |
Accept-Encoding |
||
X-AspNet-Version |
2.0.50727 |
||
X-Powered-By |
ASP.NET |
2.不能獲取html網頁信息時的解決
使用con.setRequestProperty("User-Agent", "Mozilla/5.0 (Linux; U; Android %s)");設置相關的http請求頭
五.網絡錯誤
1.不能連接到網站,總是出現連接超時
LogCat提示錯誤:
org.jsoup.HttpStatusException: HTTP error fetching URL. Status=404, URL=http://61.139.105.130:8080/zzxy/marks/index!list.action/?query=Java
解決辦法
查看所使用的url是否正確,如下
如果使用的Url是:http://rwxy.suse.edu.cn/images/Maincn.asp/
去掉最後的”/”反斜槓,http://rwxy.suse.edu.cn/images/Maincn.asp
設置相應的屬性
1)請求參數
2)User-Agent
3)Cookie
4)auth
JSoup.get().data("query","Java")//請求參數
.userAgent("Mozilla")//設置USER-AGENT
.cookie("auth", "token")//設置cookie
2.不同網絡下的網絡訪問方式導致的錯誤