內容簡介
本文通過建立一個簡單的Servlet服務器來分析安卓上用HTTP和服務器通信的細節,旨在演示C/S模式下服務器端和客戶端的工作過程。
目錄
part.1 用MyEclipse建立一個簡單的servlet服務器
注:這里首先假設您已經正確安裝好了MyEclipse及Tomcat並做了相應的配置,可以支持開發並部署一個簡單的Java Web工程;假設您已經安裝了Eclipse並配置好Android相應開發環境。
part.1 用MyEclipse建立一個簡單的servlet服務器
在MyEclipse中File->New->Other->Web Project->Next->Project Name取beautifulzzzz(隨便)->Finish,從而新建一個Java Web Project。
在web.xml中<welcome-file-list>標簽對中指明了打開網站的首頁為index.jsp,接着點擊1號按鈕選擇一個服務器,然后點擊2號按鈕將web工程部署到該服務器上,然后在瀏覽器中輸入http://localhost:8080/beautifulzzzz/就能看到相應的頁面:
現在在src中新建一個名為hello的servlet,並添加相應函數(最終如下):
1 import java.io.IOException; 2 import java.io.PrintWriter; 3 import java.util.Map; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class hello extends HttpServlet { 11 12 /** 13 * Constructor of the object. 14 */ 15 public hello() { 16 super(); 17 } 18 19 /** 20 * Destruction of the servlet. <br> 21 */ 22 public void destroy() { 23 super.destroy(); // Just puts "destroy" string in log 24 // Put your code here 25 } 26 27 /** 28 * The doGet method of the servlet. <br> 29 * 30 * This method is called when a form has its tag value method equals to get. 31 * 32 * @param request 33 * the request send by the client to the server 34 * @param response 35 * the response send by the server to the client 36 * @throws ServletException 37 * if an error occurred 38 * @throws IOException 39 * if an error occurred 40 */ 41 /* 42 * 以Get方式訪問頁面時執行該函數 執行doGet前會先執行getLastModified,如果瀏覽器發現getLastModified返回數值 43 * 與上次訪問返回數值相同,則認為該文檔沒有更新,瀏覽器執行緩存而不執行doGet 如果返回-1則認為是實時更新的,總是執行該函數 44 */ 45 public void doGet(HttpServletRequest request, HttpServletResponse response) 46 throws ServletException, IOException { 47 this.log("執行 doGet 方法..."); 48 this.execute(request, response); 49 } 50 51 /** 52 * The doPost method of the servlet. <br> 53 * 54 * This method is called when a form has its tag value method equals to 55 * post. 56 * 57 * @param request 58 * the request send by the client to the server 59 * @param response 60 * the response send by the server to the client 61 * @throws ServletException 62 * if an error occurred 63 * @throws IOException 64 * if an error occurred 執行前不會執行getLastModified 65 */ 66 public void doPost(HttpServletRequest request, HttpServletResponse response) 67 throws ServletException, IOException { 68 this.log("執行 doPost 方法..."); 69 this.execute(request, response); 70 } 71 72 /** 73 * 返回該Servlet生成文檔的更新時間。對Get方法有效 返回的時間為相對於1970年1月1日08:00:00的毫秒數 74 * 如果返回-1表示實時更新。默認為-1 75 */ 76 @Override 77 public long getLastModified(HttpServletRequest request) { 78 this.log("執行 getLastModified 方法..."); 79 return -1; 80 } 81 82 // 執行方法 83 private void execute(HttpServletRequest request, 84 HttpServletResponse response) throws ServletException, IOException { 85 86 response.setCharacterEncoding("UTF-8");// 設置request和response編碼,兩個都要注意 87 request.setCharacterEncoding("UTF-8"); 88 String requestURI = request.getRequestURI();// 訪問Servlet的URI 89 String method = request.getMethod();// 訪問Servlet的方式Get或Post 90 // 獲得用戶提交的所有param 91 Map<String, String> map = request.getParameterMap(); 92 for (String key : map.keySet()) { 93 System.out.println(key + "+" + request.getParameter(key)); 94 } 95 96 response.setContentType("text/html");// 設置文檔類型為HTML類型 97 PrintWriter out = response.getWriter(); 98 out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); 99 out.println("<HTML>"); 100 out.println("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">"); 101 out.println("<HEAD><TITLE>A Servlet</TITLE></HEAD>"); 102 out.println(" <BODY>"); 103 out.println(" 以" + method + " 方式訪問該頁面。提取的param參數為:<br/>"); 104 for (String key : map.keySet()) { 105 out.println(" " + key + "+" + request.getParameter(key) + "<br/>"); 106 } 107 108 out.println(" </BODY>"); 109 out.println("</HTML>"); 110 out.flush(); 111 out.close(); 112 } 113 114 /** 115 * Initialization of the servlet. <br> 116 * 117 * @throws ServletException 118 * if an error occurs 119 */ 120 public void init() throws ServletException { 121 // Put your code here 122 } 123 124 }
這里將doGet和doPost都交給了execute執行,在execute中用request獲取請求的相關信息,用response設置返回信息(這樣如果用瀏覽器訪問該網頁時一般是HTTP的GET或POST請求將觸發doGet或doPost函數,然后最終將請求提交給execute執行處理,在execute中獲取請求信息並用response的response.getWriter()向客戶端寫回信息,這里由於是web服務,所以寫回的是一個完整的html文檔,這樣瀏覽器就能根據返回的文檔進行相應顯示啦~)
此外,當我們添加一個servlet時會發現web.xml中多了些東西:
包括servlet的name和對應的class,特別重要的是下面的servlet-mapping中的url-pattern,這個指明了訪問該servlet的地址:在這里為http://localhost:8080/beautifulzzzz/servlet/hello
摘自園友lingyun1120的關於安卓HTTP的POST和GET的請求的總結:
1.get是從服務器上獲取數據,post是向服務器傳送數據。
2.get是把參數數據隊列加到提交表單的 ACTION屬性所指的URL中,值和表單內各個字段一一對應,在URL中可以看到。post是通過HTTPpost機制,將表單內各個字段與其內容放置 在HTML HEADER內一起傳送到ACTION屬性所指的URL地址。用戶看不到這個過程。
3.對於get方式,服務器端用 Request.QueryString獲取變量的值,對於post方式,服務器端用Request.Form獲取提交的數據。
4.get 傳送的數據量較小,不能大於2KB。post傳送的數據量較大,一般被默認為不受限制。但理論上,IIS4中最大量為80KB,IIS5中為100KB。
5.get安全性非常低,post安全性較高。
對於安卓HTTP請求的實現主要有兩種方法,一種是傳統的HttpURLConnection 方式,另一種是HttpClinet方式。
方式一:HttpURLConnection之GET
1 /*** 2 * 用HttpURLConnection發送Get請求,返回請求字符 3 * @return 4 * @throws IOException 5 */ 6 public String Func1() throws IOException{ 7 // 拼湊get請求的URL字串,使用URLEncoder.encode對特殊和不可見字符進行編碼 8 String MyURL=BASE_URL+ "?name=" + URLEncoder.encode("beautifulzzzz", "utf-8") 9 +"&password=12345678";//(好像這里中文不行) 10 URL getUrl = new URL(MyURL); 11 // 根據拼湊的URL,打開連接,URL.openConnection函數會根據URL的類型, 12 // 返回不同的URLConnection子類的對象,這里URL是一個http,因此實際返回的是HttpURLConnection 13 HttpURLConnection conn = (HttpURLConnection) getUrl.openConnection(); 14 15 // 設置連接屬性 16 conn.setConnectTimeout(30000);// 設置連接超時時長,單位毫秒 17 18 // 進行連接,但是實際上get request要在下一句的connection.getInputStream()函數中才會真正發到服務器 19 BufferedReader reader = new BufferedReader(new InputStreamReader( 20 conn.getInputStream()));// 取得輸入流,並使用Reader讀取 21 String result = ""; 22 String line = ""; 23 while ((line = reader.readLine()) != null) { 24 result = result + line+"\n"; 25 } 26 System.out.println(result); 27 reader.close(); 28 conn.disconnect(); 29 return result; 30 }
因為Get請求請求的內容是放在URL中的,所以第8行用BASE_URL和想發送的鍵值對合成為新的URL,然后根據新合成的URL打開鏈接獲得HttpURLConnection,但是真正的get請求是在connection.getInputStream()函數中才會真正發到服務器的,當該函數執行完時會返回一個輸入流,然后我們使用Reader讀取該輸入流中的內容從而獲得服務器的response。
方式二:HttpURLConnection之POST
1 /*** 2 * 用HttpURLConnection發送post請求,返回請求字符 3 * @return 4 * @throws IOException 5 */ 6 public String Func2() throws IOException { 7 URL url = new URL(BASE_URL); 8 // 此處的urlConnection對象實際上是根據URL的 9 // 請求協議(此處是http)生成的URLConnection類 10 // 的子類HttpURLConnection,故此處最好將其轉化 11 // 為HttpURLConnection類型的對象,以便用到 12 // HttpURLConnection更多的API.如下: 13 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 14 15 // 設置連接屬性 16 conn.setDoOutput(true);// 使用 URL 連接進行輸出 17 conn.setDoInput(true);// 使用 URL 連接進行輸入 18 conn.setUseCaches(false);// POST請求不能用緩存 19 conn.setConnectTimeout(30000);// 設置連接超時時長,單位毫秒 20 conn.setInstanceFollowRedirects(true);// URLConnection.setInstanceFollowRedirects是成員函數,僅作用於當前函數 21 // 配置本次連接的Content-type,配置為application/x-www-form-urlencoded的 22 // 意思是正文是urlencoded編碼過的form參數,下面我們可以看到我們對正文內容使用URLEncoder.encode 23 // 進行編碼 24 conn.setRequestProperty("Content-Type", 25 "application/x-www-form-urlencoded"); 26 conn.setRequestMethod("POST");// 設置請求方式,POST or 27 // GET,注意:如果請求地址為一個servlet地址的話必須設置成POST方式 28 29 OutputStream outStrm = conn.getOutputStream();// 此處getOutputStream會隱含的進行connect 30 DataOutputStream out = new DataOutputStream(outStrm); 31 // 正文,正文內容其實跟get的URL中'?'后的參數字符串一致 32 String content = "name=" + URLEncoder.encode("李某人", "utf-8") 33 +"&password="+ URLEncoder.encode("12345678", "utf-8"); 34 // DataOutputStream.writeBytes將字符串中的16位的unicode字符以8位的字符形式寫道流里面 35 out.writeBytes(content); 36 out.flush(); 37 out.close(); // flush and close 38 39 // 調用HttpURLConnection連接對象的getInputStream()函數, 40 // 將內存緩沖區中封裝好的完整的HTTP請求電文發送到服務端。 41 InputStream inStrm = conn.getInputStream(); // <===注意,實際發送請求的代碼段就在這里 42 // 上邊的httpConn.getInputStream()方法已調用,本次HTTP請求已結束,再向對象輸出流的輸出已無意義, 43 // 既使對象輸出流沒有調用close()方法,下邊的操作也不會向對象輸出流寫入任何數據. 44 // 因此,要重新發送數據時需要重新創建連接、重新設參數、重新創建流對象、重新寫數據、 45 // 重新發送數據(至於是否不用重新這些操作需要再研究) 46 BufferedReader reader = new BufferedReader( 47 new InputStreamReader(inStrm)); 48 String result = ""; 49 String line = ""; 50 while ((line = reader.readLine()) != null) { 51 result = result + line+"\n"; 52 } 53 System.out.println(result); 54 reader.close(); 55 conn.disconnect(); 56 return result; 57 }
對於POST請求和GET不同點在於POST的請求正文不是放在URL中。其信息包括請求頭和請求正文,所有關於此次http請求的配置都在http頭里面定義;對於請求正文content,在connect()函數里面,會根據HttpURLConnection對象的配置值生成http頭,因此在調用connect函數之前,就必須把所有的配置准備好(但是如果使用了conn.getInputStream()函數就可以不用使用connect()函數了)。
緊接着http頭的是http請求的正文,正文的內容通過outputStream寫入,實際上outputStream不是一個網絡流,充其量是個字符串流,往里面寫入的東西不會立即發送到網絡,而是在流關閉后,根據輸入的內容生成http正文。
至此,http請求的東西已經准備就緒。在getInputStream()函數調用的時候,就會把准備好的http請求正式發送到服務器了,然后返回一 個輸入流,用於讀取服務器對於此次http請求的返回信息。由於http請求在getInputStream的時候已經發送出去了(包括http頭和正 文),因此在getInputStream()函數之后對connection對象進行設置(對http頭的信息進行修改)或者寫入 outputStream(對正文進行修改)都是沒有意義的了,執行這些操作會導致異常的發生。
注:這里要注意24和35行,如果設置不對會導致服務器無法獲取鍵值對!
注:上面一段參考博客pandazxx的專欄:[Http學習之使用HttpURLConnection發送post和get請求 ]
方式三:HttpClinet之GET
1 /*** 2 * 使用Http的GET請求返回服務器返回結果字符串 3 * 4 * @return 5 * @throws ClientProtocolException 6 * @throws IOException 7 */ 8 public String Func3() throws ClientProtocolException, IOException { 9 HttpGet httpGet = new HttpGet(BASE_URL + "?name=beautifulzzzz" 10 + "&password=1234"); 11 // 獲取HttpClient對象 12 HttpClient httpClient = new DefaultHttpClient(); 13 // 連接超時 14 httpClient.getParams().setParameter( 15 CoreConnectionPNames.CONNECTION_TIMEOUT, 30000); 16 // 請求超時 17 httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 18 30000); 19 HttpResponse httpResp = httpClient.execute(httpGet); 20 String response = EntityUtils.toString(httpResp.getEntity(), "UTF-8"); 21 System.out.println(response); 22 if (response == null) 23 response = ""; 24 return response; 25 }
當使用HttpClient發送Get請求時則相對簡單,但是從第9行還是可以看出Get請求的消息還是放在URL中的,特別的這里是實例化一個HttpGet請求,並用HttpClient對象進行相關屬性配置,然后調用execute函數獲得服務器返回HttpResponse,然后調用getEntity()函數獲取Httpresponse實體內容。
方式四:HttpClinet之POST
1 /*** 2 * 使用Http的POST請求返回服務器返回結果字符串 3 * 4 * @return 5 * @throws ClientProtocolException 6 * @throws IOException 7 */ 8 public String Func4() throws ClientProtocolException, IOException { 9 // 將用戶名、密碼和imei封裝到list中,待http發送post請求給服務器 10 NameValuePair pair1 = new BasicNameValuePair("user_name", "濤"); 11 NameValuePair pair2 = new BasicNameValuePair("user_password", 12 "Deddd344"); 13 List<NameValuePair> pairList = new ArrayList<NameValuePair>(); 14 pairList.add(pair1); 15 pairList.add(pair2); 16 HttpPost httpPost = new HttpPost(BASE_URL); 17 HttpEntity requestHttpEntity = new UrlEncodedFormEntity(pairList, 18 HTTP.UTF_8); 19 // 將請求體內容加入請求中 20 httpPost.setEntity(requestHttpEntity); 21 // 獲取HttpClient對象 22 HttpClient httpClient = new DefaultHttpClient(); 23 // 連接超時 24 httpClient.getParams().setParameter( 25 CoreConnectionPNames.CONNECTION_TIMEOUT, 30000); 26 // 請求超時 27 httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 28 30000); 29 30 HttpResponse httpResp = httpClient.execute(httpPost); 31 String response = EntityUtils.toString(httpResp.getEntity(), "UTF-8"); 32 System.out.println(response); 33 if (response == null) 34 response = ""; 35 return response; 36 }
對於HttpClinet發送POST請求,因為鍵值不是保存在URL中,所以這里要用NameValuePair構建鍵值對,然后用List<NameValuePair>存儲這些鍵值對,並用此List構建一個HttpEntity實體信息,然后調用httpPost.setEntity(requestHttpEntity);將實體信息加入httpPost中,接着同Get利用execute執行POST請求,然后調用getEntity()獲得服務器返回的實體信息。
如下圖:本文的安卓客戶端分別用上述講的四種方法向本地的Java Web進行訪問,圖中顯示為執行URI_GET請求時的客戶端和服務器后台的效果:點擊URI_GET按鈕-->客戶端啟動Quest(1)線程使用Func1()進行Get請求-->當請求結束通過Message將從Func1()返回的服務器返回的Response字符串傳送給消息接收句柄,在消息接受句柄中進行對UI中的TextView更新,顯示返回結果。而服務器端如part1中介紹,當接收到客戶端的POST或GET請求時,都會委托給execute函數來處理,並用PrintWriter out = response.getWriter();將消息發給客戶端。
拓展知識均來自網絡:請支持原創作者。
http://www.apkbus.com/android-13575-1-1.html
第一種版本:
- HTTP 定義了與服務器交互的不同方法,最基本的方法是 GET 和 POST。
- 事實上 GET 適用於多數請求,而保留 POST 僅用於更新站點。根據 HTTP 規范,GET 用於信息獲取,而且應該是 安全的和 冪等的。所謂安全的意味着該操作用於獲取信息而非修改信息。換句話說,GET 請求一般不應產生副作用。冪等的意味着對同一 URL 的多個請求應該返回同樣的結果。完整的定義並不像看起來那樣嚴格。從根本上講,其目標是當用戶打開一個鏈接時,它可以確信從自身的角度來看沒有改變資源。 比如,新聞站點的頭版不斷更新。雖然第二次請求會返回不同的一批新聞,該操作仍然被認為是安全的和冪等的,因為它總是返回當前的新聞。反之亦然。
- POST 請求就不那么輕松了。POST 表示可能改變服務器上的資源的請求。仍然以新聞站點為例,讀者對文章的注解應該通過 POST 請求實現,因為在注解提交之后站點已經不同了(比方說文章下面出現一條注解);
- 在FORM提交的時候,如果不指定Method,則默認為GET請求,Form中提交的數據將會附加在url之后,以?分開與url分開。字母數字字符原 樣發送,但空格轉換為“+“號,其它符號轉換為%XX,其中XX為該符號以16進制表示的ASCII(或ISO Latin-1)值。GET請求請提交的數據放置在HTTP請求協議頭中,而POST提交的數據則放在實體數據中;
- GET方式提交的數據最多只能有1024字節,而POST則沒有此限制。
第二種版本:
- get是從服務器上獲取數據,post是向服務器傳送數據。
- 在客戶端,Get方式在通過URL提交數據,數據在URL中可以看到;POST方式,數據放置在HTML HEADER內提交。
- 對於get方式,服務器端用Request.QueryString獲取變量的值,對於post方式,服務器端用Request.Form獲取提交的數據。
- GET方式提交的數據最多只能有1024字節,而POST則沒有此限制。
- 安全性問題。正如在(1)中提到,使用 Get 的時候,參數會顯示在地址欄上,而 Post 不會。所以,如果這些數據是中文數據而且是非敏感數據,那么使用 get;如果用戶輸入的數據不是中文字符而且包含敏感數據,那么還是使用 post為好。
第三種版本:
- Get是用來從服務器上獲得數據,而Post是用來向服務器上傳遞數據。
- Get將表單中數據的按照variable=value的形式,添加到action所指向的URL后面,並且兩者使用“?”連接,而各個變量之間使用 “&”連接;Post是將表單中的數據放在form的數據體中,按照變量和值相對應的方式,傳遞到action所指向URL。
- Get是不安全的,因為在傳輸過程,數據被放在請求的URL中,而如今現有的很多服務器、代理服務器或者用戶代理都會將請求URL記錄到日志文件中,然后 放在某個地方,這樣就可能會有一些隱私的信息被第三方看到。另外,用戶也可以在瀏覽器上直接看到提交的數據,一些系統內部消息將會一同顯示在用戶面前。 Post的所有操作對用戶來說都是不可見的。
- Get傳輸的數據量小,這主要是因為受URL長度限制;而Post可以傳輸大量的數據,所以在上傳文件只能使用Post(當然還有一個原因,將在后面的提到)。
- Get限制Form表單的數據集的值必須為ASCII字符;而Post支持整個ISO10646字符集。
- Get是Form的默認方法。
相關鏈接
此外推薦一些鏈接幫助更好理解安卓GET和POST請求:
1、我的漫漫程序之旅:[http://www.blogjava.net/supercrsky/articles/247449.html]
內容提示:給出了JDK中的URLConnection參數詳解,寫的很詳細,能幫助理解URLConnection
2、pandazxx的專欄:[http://blog.csdn.net/pandazxx/article/details/1657109]
內容提示:Http學習之使用HttpURLConnection發送post和get請求 ,有例子,有注釋
3、上述工程C/S代碼:[http://pan.baidu.com/s/1qWqNUos]
4、上述工程GitHub:[https://github.com/beautifulzzzz/Android/tree/master/HTTP_POST_GET]