還是網絡

客戶端:

***
*HttpURLConnection連接服務器<br>
*<功能詳細描述><br>
*1、通過後臺得到sessionID<br>
*2、檢查MAC地址是否正確<br>
*3、處理從服務器讀取的JSON對象<br>
*4、從服務器讀取對象<br>
*5、得到對象輸出流<br>
*6、設置HttpURLConnection參數<br>

* @author "zhaohaiyang"<br>
*@version 版本號 2010-1-14 下午02:01:41<br>
*@see 相關類/方法<br>
**/
public class ConUtils
{

 /**
*通過後臺得到sessionID<br>
*<功能詳細描述><br>

* @param parameters
*            登陸信息
* @return 登陸成功返回sessionId 失敗返回“”
* @see [類、類#方法、類#成員]
*/
public static String receiveSessionID(String[] parameters, String[] values)
{
String tempSessionId = "";// SessionID

  URL url = null;// 請求處理的Servlet

  ObjectOutputStream objOutputStrm = null;// 對象輸出流

  InputStream inStrm = null;// 得到HttpURLConnection的輸入流

  HttpURLConnection httpUrlConnection = null;

  try
{
url = new URL("http://192.168.18.109:8080/jj_erp/loginval");

   // 設置HttpURLConnection參數
httpUrlConnection = setURLConnectionProperties(url);

   // 得到對象輸出流
objOutputStrm = getObjOutStream(httpUrlConnection);

   JSONObject obj = new JSONObject();
for (int i = 0; i < parameters.length; i++)
{
obj.put(parameters[i], values[i]);
}
// 向對象輸出流寫出數據,這些數據將存到內存緩衝區中
objOutputStrm.writeObject(obj.toString());
// 刷新對象輸出流,將任何字節都寫入潛在的流中(些處爲ObjectOutputStream)
objOutputStrm.flush();
// 關閉流對象。此時,不能再向對象輸出流寫入任何數據,先前寫入的數據存在於內存緩衝區中,
// 在調用下邊的getInputStream()函數時才把準備好的http請求正式發送到服務器
// objOutputStrm.close();

   // 調用HttpURLConnection連接對象的getInputStream()函數,
// 將內存緩衝區中封裝好的完整的HTTP請求電文發送到服務端。
inStrm = httpUrlConnection.getInputStream(); // <===注意,實際發送請求的代碼段就在這裏

   // 上邊的httpConn.getInputStream()方法已調用,本次HTTP請求已結束,下邊向對象輸出流的輸出已無意義,
// 既使對象輸出流沒有調用close()方法,下邊的操作也不會向對象輸出流寫入任何數據.
// 因此,要重新發送數據時需要重新創建連接、重新設參數、重新創建流對象、重新寫數據、
// 重新發送數據(至於是否不用重新這些操作需要再研究)
// objOutputStrm.writeObject(new String(""));
// httpUrlConnection.getInputStream();

   // 從服務器讀取對象
Object inObj = readObjectFromServer(inStrm);
// 處理從服務器讀取的JSON對象
tempSessionId = doJsonObjectFromServerForSesId(tempSessionId, inObj);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (ProtocolException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (JSONException e)
{
e.printStackTrace();
}
finally
{
try
{
if (objOutputStrm != null)
{
objOutputStrm.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
if (inStrm != null)
{
inStrm.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}

  return tempSessionId;
}

 /**
*檢查MAC地址是否正確<br>

* @param mac
* @return MAC地址正確返回true 錯誤返回false
*@see [類、類#方法、類#成員]
*/
public static boolean checkMac(String mac)
{

  URL url = null;// 請求處理的Servlet

  boolean flag = false;// MAC地址是否正確

  ObjectOutputStream objOutputStrm = null;// 對象輸出流

  InputStream inStrm = null;// 得到HttpURLConnection的輸入流

  HttpURLConnection httpUrlConnection = null;
try
{
url = new URL("http://192.168.18.109:8080/jj_erp/checkMac");

   // 設置HttpURLConnection參數
httpUrlConnection = setURLConnectionProperties(url);

   // 得到對象輸出流
objOutputStrm = getObjOutStream(httpUrlConnection);

   JSONObject obj = new JSONObject();
obj.put("mac", mac);
// 向對象輸出流寫出數據,這些數據將存到內存緩衝區中
objOutputStrm.writeObject(obj.toString());
// 刷新對象輸出流,將任何字節都寫入潛在的流中(些處爲ObjectOutputStream)
objOutputStrm.flush();
// 關閉流對象。此時,不能再向對象輸出流寫入任何數據,先前寫入的數據存在於內存緩衝區中,
// 在調用下邊的getInputStream()函數時才把準備好的http請求正式發送到服務器
// objOutputStrm.close();

   // 調用HttpURLConnection連接對象的getInputStream()函數,
// 將內存緩衝區中封裝好的完整的HTTP請求電文發送到服務端。
inStrm = httpUrlConnection.getInputStream(); // <===注意,實際發送請求的代碼段就在這裏

   // 上邊的httpConn.getInputStream()方法已調用,本次HTTP請求已結束,下邊向對象輸出流的輸出已無意義,
// 既使對象輸出流沒有調用close()方法,下邊的操作也不會向對象輸出流寫入任何數據.
// 因此,要重新發送數據時需要重新創建連接、重新設參數、重新創建流對象、重新寫數據、
// 重新發送數據(至於是否不用重新這些操作需要再研究)
// objOutputStrm.writeObject(new String(""));
// httpUrlConnection.getInputStream();

   // 從服務器讀取對象
Object inObj = readObjectFromServer(inStrm);
// 處理從服務器讀取的JSON對象
flag = doJsonObjectFromServer(flag, inObj);

  }
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (JSONException e)
{
e.printStackTrace();
}
finally
{

  }

  return flag;
}

 /**
*處理從服務器讀取的JSON對象 用於校驗MAC地址<br>
*<功能詳細描述><br>

* @param flag
*            MAC是否正確
* @param inObj
*            從服務器讀到的JSON對象
* @return MAC是否正確
* @throws JSONException
*@see [類、類#方法、類#成員]
*/

 private static boolean doJsonObjectFromServer(boolean flag, Object inObj)
throws JSONException
{
// 做非空處理
if (inObj != null)
{
// 根據得到的序列化對象 構建JSON對象
JSONObject injson = new JSONObject(inObj.toString());
// 拿到JSON對象中 對應key的值
String getStr = injson.getString("returnstring");
if (getStr.equals("true"))
{
flag = true;
}
}
return flag;
}

 private static String doJsonObjectFromServerForSesId(String tempSessionID,
Object inObj) throws JSONException
{
// 做非空處理
if (inObj != null)
{
// 根據得到的序列化對象 構建JSON對象
JSONObject injson = new JSONObject(inObj.toString());
// 拿到JSON對象中 對應key的值
tempSessionID = injson.getString("sessionID");
}
return tempSessionID;
}

 /**
*從服務器讀取對象<br>
*<功能詳細描述><br>

* @param inStrm
*            輸入流
* @return 從服務器返回的對象
* @throws IOException
*@see [類、類#方法、類#成員]
*/

 private static Object readObjectFromServer(InputStream inStrm)
throws IOException
{
ObjectInputStream objInStream; // 輸入流 從服務器讀取JSON對象
objInStream = new ObjectInputStream(inStrm);// 輸入流 從服務器讀取JSON對象
Object inObj = null;
try
{
inObj = objInStream.readObject();// 讀取對象
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
return inObj;
}

 /**
*得到對象輸出流<br>
*<功能詳細描述><br>

* @param httpUrlConnection
*            後臺與服務器之間的通信
* @return 對象輸出流
* @throws IOException
*@see [類、類#方法、類#成員]
*/

 private static ObjectOutputStream getObjOutStream(
HttpURLConnection httpUrlConnection) throws IOException
{
OutputStream outStrm;// 得到HttpURLConnection的輸出流
ObjectOutputStream objOutputStrm;// 對象輸出流
// 此處getOutputStream會隱含的進行connect(即:如同調用上面的connect()方法,
// 所以在開發中不調用上述的connect()也可以)。
outStrm = httpUrlConnection.getOutputStream();

  // 現在通過輸出流對象構建對象輸出流對象,以實現輸出可序列化的對象。
// 使用JSON傳值
objOutputStrm = new ObjectOutputStream(outStrm);
return objOutputStrm;
}

 /**
*設置HttpURLConnection參數<br>
*<功能詳細描述><br>

* @param url
*            請求處理的地址
* @return 後臺與服務器之間的通信連接
* @throws IOException
* @throws ProtocolException
*@see [類、類#方法、類#成員]
*/

 private static HttpURLConnection setURLConnectionProperties(URL url)
throws IOException, ProtocolException
{
HttpURLConnection httpUrlConnection;
URLConnection rulConnection = url.openConnection();// 此處的urlConnection對象實際上是根據URL的
// 請求協議(此處是http)生成的URLConnection類
// 的子類HttpURLConnection,故此處最好將其轉化
// 爲HttpURLConnection類型的對象,以便用到
// HttpURLConnection更多的API.如下:

  httpUrlConnection = (HttpURLConnection) rulConnection;

  // 設置是否向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");
//   
httpUrlConnection
.setRequestProperty("Content-type", "application/json");

  // 設定請求的方法爲"POST",默認是GET
httpUrlConnection.setRequestMethod("POST");

  try
{
// 連接,從上述至此的配置必須要在connect之前完成,
httpUrlConnection.connect();
httpUrlConnection.setConnectTimeout(1);
httpUrlConnection.setReadTimeout(1);
}
catch (ConnectException e1)
{
if (e1.getMessage().equals("Connection refused: connect"))
{
JOptionPane.showMessageDialog(null, "連接超時");
System.exit(0);
}
}
return httpUrlConnection;
}

 public static void main(String[] args)
{
if (checkMac("40-61-86-69-82-E2"))
{
System.out.println("mac地址校驗成功");
}
else
{
System.out.println("mac地址校驗失敗");
}
}
}

 

服務器端:

checkMac.java

      public class CheckMac extends HttpServlet
{
private static final long serialVersionUID = 1L;
private String    returnstring  = "false";

 @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
InputStream inStream = req.getInputStream();
ObjectInputStream objInStream = new ObjectInputStream(inStream);
Object obj = null;
try
{
obj = objInStream.readObject();
}
catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
JSONObject json = null;
String mac = "";
JSONObject outjson = new JSONObject();
try
{
if (obj != null)
{
json = new JSONObject(obj.toString());
mac = json.getString("mac");

    if (mac.equals("40-61-86-69-82-E2"))
{
returnstring = "true";
}
}
}
catch (JSONException e)
{
e.printStackTrace();
}
try
{
outjson.put("returnstring", returnstring);
}
catch (JSONException e)
{
e.printStackTrace();
}
resp.setContentType("text/html;charset=utf-8");
OutputStream out = resp.getOutputStream();
ObjectOutputStream objOutputStrm = new ObjectOutputStream(out);
objOutputStrm.writeObject(outjson.toString());
objOutputStrm.flush();
objOutputStrm.close();
}

 

LoginValidate.java

private String    dept    = ""; // 部門
private String    name    = ""; // 姓名
private String    pass    = ""; // 密碼
private String    mac     = ""; // MAC地址
private String    ip     = ""; // IP地址
private String    sessionID   = "";

 @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
InputStream inStream = req.getInputStream();
ObjectInputStream objInStream = new ObjectInputStream(inStream);
Object obj = null;
try
{
obj = objInStream.readObject();
}
catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
JSONObject json = null;
JSONObject outjson = new JSONObject();
try
{
if (obj != null)
{
json = new JSONObject(obj.toString());
if (json != null)
{
dept = json.getString("dept");
name = json.getString("name");
pass = json.getString("pass");
mac = json.getString("mac");
ip = json.getString("ip");
}
}
}
catch (JSONException e)
{
e.printStackTrace();
}

  /**
* 判斷登陸信息 登陸成功創建Session
*/
if (validateInfo())
{
//   HttpSession session = req.getSession(true);
//   sessionID = session.getId();
sessionID = "sessionid";
}
// 把sessionID放入JSON中
try
{
outjson.put("sessionID", sessionID);
}
catch (JSONException e)
{
e.printStackTrace();
}

  // 將sessionID以JSON方式發送到客戶端
resp.setContentType("text/html;charset=utf-8");
OutputStream out = resp.getOutputStream();
ObjectOutputStream objOutputStrm = new ObjectOutputStream(out);
objOutputStrm.writeObject(outjson.toString());
objOutputStrm.flush();
objOutputStrm.close();
}

 /**
*校驗登陸信息是否正確<br>
*<功能詳細描述><br>

* @return 正確返回true 否則返回false
*@see [類、類#方法、類#成員]
*/

 private boolean validateInfo()
{
return true;
}


最常用的Http請求無非是get和post,get請求可以獲取靜態頁面,也可以把參數放在URL字串後面,傳遞給servlet,post與get的不同之處在於post的參數不是放在URL字串裏面,而是放在http請求的正文內。
在Java中可以使用HttpURLConnection發起這兩種請求,瞭解此類,對於瞭解soap,和編寫servlet的自動測試代碼都有很大的幫助。
下面的代碼簡單描述瞭如何使用HttpURLConnection發起這兩種請求,以及傳遞參數的方法:
public class HttpInvoker ...{

public static final String GET_URL = "http://localhost:8080/welcome1";

public static final String POST_URL = "http://localhost:8080/welcome1";

public static void readContentFromGet() throws IOException ...{
// 拼湊get請求的URL字串,使用URLEncoder.encode對特殊和不可見字符進行編碼
String getURL = GET_URL + "?username="
+ URLEncoder.encode("fat man", "utf-8");
URL getUrl = new URL(getURL);
// 根據拼湊的URL,打開連接,URL.openConnection函數會根據URL的類型,
// 返回不同的URLConnection子類的對象,這裏URL是一個http,因此實際返回的是HttpURLConnection
HttpURLConnection connection = (HttpURLConnection) getUrl
.openConnection();
// 進行連接,但是實際上get request要在下一句的connection.getInputStream()函數中才會真正發到
// 服務器
connection.connect();
// 取得輸入流,並使用Reader讀取
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
System.out.println("=============================");
System.out.println("Contents of get request");
System.out.println("=============================");
String lines;
while ((lines = reader.readLine()) != null) ...{
System.out.println(lines);
}
reader.close();
// 斷開連接
connection.disconnect();
System.out.println("=============================");
System.out.println("Contents of get request ends");
System.out.println("=============================");
}

public static void readContentFromPost() throws IOException ...{
// Post請求的url,與get不同的是不需要帶參數
URL postUrl = new URL(POST_URL);
// 打開連接
HttpURLConnection connection = (HttpURLConnection) postUrl
.openConnection();
// Output to the connection. Default is
// false, set to true because post
// method must write something to the
// connection
// 設置是否向connection輸出,因爲這個是post請求,參數要放在
// http正文內,因此需要設爲true
connection.setDoOutput(true);
// Read from the connection. Default is true.
connection.setDoInput(true);
// Set the post method. Default is GET
connection.setRequestMethod("POST");
// Post cannot use caches
// Post 請求不能使用緩存
connection.setUseCaches(false);
// This method takes effects to
// every instances of this class.
// URLConnection.setFollowRedirects是static函數,作用於所有的URLConnection對象。
// connection.setFollowRedirects(true);

// This methods only
// takes effacts to this
// instance.
// URLConnection.setInstanceFollowRedirects是成員函數,僅作用於當前函數
connection.setInstanceFollowRedirects(true);
// Set the content type to urlencoded,
// because we will write
// some URL-encoded content to the
// connection. Settings above must be set before connect!
// 配置本次連接的Content-type,配置爲application/x-www-form-urlencoded的
// 意思是正文是urlencoded編碼過的form參數,下面我們可以看到我們對正文內容使用URLEncoder.encode
// 進行編碼
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// 連接,從postUrl.openConnection()至此的配置必須要在connect之前完成,
// 要注意的是connection.getOutputStream會隱含的進行connect。
connection.connect();
DataOutputStream out = new DataOutputStream(connection
.getOutputStream());
// The URL-encoded contend
// 正文,正文內容其實跟get的URL中'?'後的參數字符串一致
String content = "firstname=" + URLEncoder.encode("一個大肥人", "utf-8");
// DataOutputStream.writeBytes將字符串中的16位的unicode字符以8位的字符形式寫道流裏面
out.writeBytes(content); 

out.flush();
out.close(); // flush and close
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
System.out.println("=============================");
System.out.println("Contents of post request");
System.out.println("=============================");
while ((line = reader.readLine()) != null) ...{
System.out.println(line);
}
System.out.println("=============================");
System.out.println("Contents of post request ends");
System.out.println("=============================");
reader.close();
connection.disconnect();
}

/** *//**
* @param args
*/
public static void main(String[] args) ...{
// TODO Auto-generated method stub
try ...{
readContentFromGet();
readContentFromPost();
} catch (IOException e) ...{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
上面的readContentFromGet()函數產生了一個get請求,傳給servlet一個username參數,值爲"fat man"。
readContentFromPost()函數產生了一個post請求,傳給servlet一個firstname參數,值爲"一個大肥人"。
HttpURLConnection.connect函數,實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。無論是post還是get,http請求實際上直到HttpURLConnection.getInputStream()這個函數裏面才正式發送出去。

readContentFromPost() 中,順序是重中之重,對connection對象的一切配置(那一堆set函數)都必須要在connect()函數執行之前完成。而對 outputStream的寫操作,又必須要在inputStream的讀操作之前。這些順序實際上是由http請求的格式決定的。

http 請求實際上由兩部分組成,一個是http頭,所有關於此次http請求的配置都在http頭裏面定義,一個是正文content,在connect()函 數裏面,會根據HttpURLConnection對象的配置值生成http頭,因此在調用connect函數之前,就必須把所有的配置準備好。

緊接着http頭的是http請求的正文,正文的內容通過outputStream寫入,實際上outputStream不是一個網絡流,充其量是個字符串流,往裏面寫入的東西不會立即發送到網絡,而是在流關閉後,根據輸入的內容生成http正文。

至 此,http請求的東西已經準備就緒。在getInputStream()函數調用的時候,就會把準備好的http請求正式發送到服務器了,然後返回一個 輸入流,用於讀取服務器對於此次http請求的返回信息。由於http請求在getInputStream的時候已經發送出去了(包括http頭和正 文),因此在getInputStream()函數之後對connection對象進行設置(對http頭的信息進行修改)或者寫入 outputStream(對正文進行修改)都是沒有意義的了,執行這些操作會導致異常的發生。

使用HttpURLConnection發送post和get請求

1、http://blog.csdn.net/pandazxx/archive/2007/06/18/1657109.aspx

2、http://blog.csdn.net/pandazxx/archive/2007/06/20/1660008.aspx 

但我們常常會碰到這樣一種情況:

通過HttpURLConnection來模擬模擬用戶登錄Web服務器,服務器使用cookie進行用戶認證。在模擬登錄時,Post表單數據後可以正確登錄(登陸成功時會response一個cookie,然後redirect到main page,不成功則redirect到login page),但是在使用HttpURLConnection再次連接服務器其他頁面(或者即使是當前的response裏是redirect的page)時,服務器都會認爲是全新的一個Session。

解決方法有2步:

1. 調用HttpURLConnection (send post request to login page)的setInstanceFollowRedirects()方法,參數爲false (這樣不會去獲取redirect page)

2. 獲取HttpURLConnection send post request to login page的session id,然後在之後每一次的connection裏都加上該session id

Example:

    String sessionId = "";  
    public static void sendLoginRequest() throws IOException {
       URL loginUrl = new URL("http://xxx");
       HttpURLConnection connection = (HttpURLConnection) loginUrl.openConnection();
 
 
       // Output to the connection. Default is
       // false, set to true because post
       // method must write something to the
       // connection
       // 設置是否向connection輸出,因爲這個是post請求,參數要放在
       // http正文內,因此需要設爲true
       connection.setDoOutput(true);
       // Read from the connection. Default is true.
       connection.setDoInput(true);
       // Set the post method. Default is GET
       connection.setRequestMethod("POST");
       // Post cannot use caches
       // Post 請求不能使用緩存
       connection.setUseCaches(false);
 
       // This method takes effects to
       // every instances of this class.
       // URLConnection.setFollowRedirectsstatic函數,作用於所有的URLConnection對象。
       // connection.setFollowRedirects(true);
      
       // This methods only
       // takes effacts to this
       // instance.
       // URLConnection.setInstanceFollowRedirects是成員函數,僅作用於當前函數
       connection.setInstanceFollowRedirects(false);
      
       // Set the content type to urlencoded,
       // because we will write
       // some URL-encoded content to the
       // connection. Settings above must be set before connect!
       // 配置本次連接的Content-type,配置爲application/x-www-form-urlencoded
       // 意思是正文是urlencoded編碼過的form參數,下面我們可以看到我們對正文內容使用URLEncoder.encode
       // 進行編碼
       connection.setRequestProperty("Content-Type",
              "application/x-www-form-urlencoded");
       // 連接,從postUrl.openConnection()至此的配置必須要在connect之前完成,
       // 要注意的是connection.getOutputStream會隱含的進行connect
       connection.connect();
 
       DataOutputStream out = new DataOutputStream(connection
              .getOutputStream());
 
         // 要傳的參數
       String content = URLEncoder.encode("username""UTF-8") + "="
              + URLEncoder.encode("XXX""UTF-8");
       content += "&" + URLEncoder.encode("password""UTF-8") + "="
              + URLEncoder.encode("XXXX""UTF-8");
 
       // DataOutputStream.writeBytes將字符串中的16位的unicode字符以8位的字符形式寫道流裏面
       out.writeBytes(content);
 
       out.flush();
       out.close(); // flush and close
 
          //Get Session ID
       String key = "";
       if (connection != null) {
           for (int i = 1; (key = connection.getHeaderFieldKey(i)) != null; i++) {
              if (key.equalsIgnoreCase("set-cookie")) {
                  sessionId = connection.getHeaderField(key);
                  sessionId = sessionId.substring(0, sessionId.indexOf(";"));
              }
           }
       }     
       connection.disconnect();
    }
 

 然後之後每一次connection都要加上這個session id:

URL url = new URL("http:......");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestProperty("Cookie",this.sessionId);
connection.connect();




  • Android HttpURLConnection
  • 的應用可以幫助我們與網絡相連,進行一些特定的操作。在這裏就先爲大家介紹一下這一應用的具體操作方法。

Android手機操作系統是一款開源的操作系統,可以靈活方便的幫助我們滿足許多需求。在這裏大家可以通過對Android HttpURLConnection應用的瞭解,來掌握讀取網絡的方法,方便大家解讀這一系統的功能。

網絡上很多關於Android HttpURLConnection的例子, 在這裏就先爲大家介紹一下。

  1. void getInput(){   
  2. try  
  3. {  
  4. URL url = new URL("http://www.google.cn/");  
  5. HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  6. conn.setDoInput(true);  
  7. conn.setConnectTimeout(10000);  
  8. conn.setRequestMethod("GET");  
  9. conn.setRequestProperty("accept", "*/*");  
  10. String location = conn.getRequestProperty("location");  
  11. int resCode = conn.getResponseCode();  
  12. conn.connect();  
  13. InputStream stream = conn.getInputStream();  
  14. byte[] data=new byte[102400];  
  15. int length=stream.read(data);  
  16. String str=new String(data,0,length);   
  17. conn.disconnect();  
  18. System.out.println(str);  
  19. stream.close();  
  20. }  
  21. catch(Exception ee)  
  22. {  
  23. System.out.print("ee:"+ee.getMessage());   
  24. }  

只是要注意的是配置一個權限,AndroidManifest.xml 中應該加入如下節點。

  1. < /activity> 
  2. < /application> 
  3. < uses-permission android:name="android.permission.INTERNET"> 
  4. < /uses-permission> 
  5. < /manifest>  

可以把AndroidManifest.xml open with manifest editor 來編輯 在permissions中add uses-permission,然後再在name中選擇Android.permission.INTERNET,然後save就ok了。

Android HttpURLConnection的基本應用就爲大家介紹到這裏。



使用URL和URLConnection(多線程下載)
2010-02-22 13:47

17.2.3 使用URL和URLConnection

URL(Uniform Resource Locator)對象代表統一資源定位器,它是指向互聯網“資源”的指針。資源可以是簡單的文件或目錄,也可以是對更爲複雜的對象引用,例如對數據庫或搜 索引擎的查詢。通常情況而言,URL可以由協議名、主機、端口和資源組成。即滿足如下格式:

protocol://host:port/resourceName

例如如下的URL地址:

http://www.oneedu.cn/Index.htm

JDK中還提供了一個URI(Uniform Resource Identifiers)類,其實例代表一個統一資源標識符,Java的URI不能用於定位任何資源,它的唯一作用就是解析。與此對應的是,URL則包含 一個可打開到達該資源的輸入流,因此我們可以將URL理解成URI的特例。

URL類提供了多個構造器用於創建URL對象,一旦獲得了URL對象之後,可以調用如下方法來訪問該URL對應的資源:

String getFile():獲取此URL的資源名。
String getHost():獲取此URL的主機名。
String getPath():獲取此URL的路徑部分。
int getPort():獲取此 URL 的端口號。
String getProtocol():獲取此 URL 的協議名稱。 
String getQuery():獲取此 URL 的查詢字符串部分。 
URLConnection openConnection():返回一個URLConnection對象,它表示到URL所引用的遠程對象的連接。 
InputStream openStream():打開與此URL的連接,並返回一個用於讀取該URL資源的InputStream。

URL對象中前面幾個方法都非常容易理解,而該對象提供的openStream()可以讀取該URL資源的InputStream,通過該方法可以非常方便地讀取遠程資源——甚至實現多線程下載。如下程序所示:

程序清單:codes/17/17-2/MutilDown.java

//定義下載從start到end的內容的線程
class DownThread extends Thread
{
//定義字節數組(取水的竹筒)的長度
private final int BUFF_LEN = 32;
//定義下載的起始點
private long start;
//定義下載的結束點
private long end;
//下載資源對應的輸入流
private InputStream is;
//將下載到的字節輸出到raf中
private RandomAccessFile raf ;
//構造器,傳入輸入流,輸出流和下載起始點、結束點
public DownThread(long start , long end
, InputStream is , RandomAccessFile raf)
{
//輸出該線程負責下載的字節位置
System.out.println(start + "---->"  + end);
this.start = start;
this.end = end;
this.is = is;
this.raf = raf;
}
public void run()
{
try
{
is.skip(start);
raf.seek(start);
//定義讀取輸入流內容的緩存數組(竹筒)
byte[] buff = new byte[BUFF_LEN];
//本線程負責下載資源的大小
long contentLen = end - start;
//定義最多需要讀取幾次就可以完成本線程的下載
long times = contentLen / BUFF_LEN + 4;
//實際讀取的字節數
int hasRead = 0;
for (int i = 0; i < times ; i++)
{
hasRead = is.read(buff);
//如果讀取的字節數小於0,則退出循環!
if (hasRead < 0)
{
break;
}
raf.write(buff , 0 , hasRead);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
//使用finally塊來關閉當前線程的輸入流、輸出流
finally
{
try
{
if (is != null)
{
is.close();
}
if (raf != null)
{
raf.close();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
}
public class MutilDown
{
public static void main(String[] args)
{
final int DOWN_THREAD_NUM = 4;
final String OUT_FILE_NAME = "down.jpg";
InputStream[] isArr = new InputStream[DOWN_THREAD_NUM];
RandomAccessFile[] outArr = new RandomAccessFile[DOWN_THREAD_NUM];
try
{
//創建一個URL對象
URL url = new URL("http://images.china-pub.com/
+ "ebook35001-40000/35850/shupi.jpg");
//以此URL對象打開第一個輸入流
isArr[0] = url.openStream();
long fileLen = getFileLength(url);
System.out.println("網絡資源的大小" + fileLen);
//以輸出文件名創建第一個RandomAccessFile輸出流
outArr[0] = new RandomAccessFile(OUT_FILE_NAME , "rw");
//創建一個與下載資源相同大小的空文件
for (int i = 0 ; i < fileLen ; i++ )
{
outArr[0].write(0);
}
//每線程應該下載的字節數
long numPerThred = fileLen / DOWN_THREAD_NUM;
//整個下載資源整除後剩下的餘數
long left = fileLen % DOWN_THREAD_NUM;
for (int i = 0 ; i < DOWN_THREAD_NUM; i++)
{
//爲每個線程打開一個輸入流、一個RandomAccessFile對象,
//讓每個線程分別負責下載資源的不同部分。
if (i != 0)
{
//以URL打開多個輸入流
isArr[i] = url.openStream();
//以指定輸出文件創建多個RandomAccessFile對象
outArr[i] = new RandomAccessFile(OUT_FILE_NAME , "rw");
}
//分別啓動多個線程來下載網絡資源
if (i == DOWN_THREAD_NUM - 1 )
{
//最後一個線程下載指定numPerThred+left個字節
new DownThread(i * numPerThred , (i + 1) * numPerThred + left
, isArr[i] , outArr[i]).start();
}
else
{
//每個線程負責下載一定的numPerThred個字節
new DownThread(i * numPerThred , (i + 1) * numPerThred,
isArr[i] , outArr[i]).start();
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
//定義獲取指定網絡資源的長度的方法
//定義獲取指定網絡資源的長度的方法
public static long getFileLength(URL url) throws Exception
{
long length = 0;
//打開該URL對應的URLConnection。
URLConnection con = url.openConnection();
//獲取連接URL資源的長度
long size = con.getContentLength();
length = size;
return length;
}
}

上面程序中定義了DownThread線程類,該線程從InputStream中讀取從start開始,到end結束的所有字節數據,並寫入RandomAccessFile對象。這個DownThread線程類的run()就是一個簡單的輸入、輸出實現。

程序中MutilDown類中的main方法負責按如下步驟來實現多線程下載:

創建URL對象。

獲取指定URL對象所指向資源的大小(由getFileLength方法實現),此處用到了URLConnection類,該類代表Java應用程序和URL之間的通信鏈接。下面還有關於URLConnection更詳細的介紹。

在本地磁盤上創建一個與網絡資源相同大小的空文件。

計算每條線程應該下載網絡資源的哪個部分(從哪個字節開始,到哪個字節結束)。

依次創建、啓動多條線程來下載網絡資源的指定部分。

上面程序已經實現了多線程下載的核心代碼,如果要實現斷點下載,則還需要額外增加一個配置文件(讀者可以發現所有斷點下載工具都會在下載開始生成兩 個文件:一個是與網絡資源相同大小的空文件,一個是配置文件),該配置文件分別記錄每個線程已經下載到了哪個字節,當網絡斷開後再次開始下載時,每個線程 根據配置文件裏記錄的位置向後下載即可。

URL的openConnection()方法將返回一個URLConnection對象,該對象表示應用程序和 URL 之間的通信鏈接。程序可以通過URLConnection實例向該URL發送請求、讀取URL引用的資源。

通常創建一個和 URL的連接,併發送請求、讀取此URL引用的資源需要如下幾個步驟:

通過調用URL對象openConnection()方法來創建URLConnection對象。

設置URLConnection的參數和普通請求屬性。

如果只是發送GET方式請求,使用connect方法建立和遠程資源之間的實際連接即可;如果需要發送POST方式的請求,需要獲取URLConnection實例對應的輸出流來發送請求參數。

遠程資源變爲可用,程序可以訪問遠程資源的頭字段或通過輸入流讀取遠程資源的數據。

在建立和遠程資源的實際連接之前,程序可以通過如下方法來設置請求頭字段:

setAllowUserInteraction:設置該URLConnection的allowUserInteraction請求頭字段的值。
setDoInput:設置該URLConnection的doInput請求頭字段的值。
setDoOutput:設置該URLConnection的doOutput請求頭字段的值。
setIfModifiedSince:設置該URLConnection的ifModifiedSince請求頭字段的值。
setUseCaches:設置該URLConnection的useCaches請求頭字段的值。

除此之外,還可以使用如下方法來設置或增加通用頭字段:

setRequestProperty(String key, String value):設置該URLConnection的key請求頭字段的值爲value。如下代碼所示:

conn.setRequestProperty("accept" , "*/*")

addRequestProperty(String key, String value):爲該URLConnection的key請求頭字段的增加value值,該方法並不會覆蓋原請求頭字段的值,而是將新值追加到原請求頭字段中。

當遠程資源可用之後,程序可以使用以下方法用於訪問頭字段和內容:

Object getContent():獲取該URLConnection的內容。
String getHeaderField(String name):獲取指定響應頭字段的值。
getInputStream():返回該URLConnection對應的輸入流,用於獲取URLConnection響應的內容。
getOutputStream():返回該URLConnection對應的輸出流,用於向URLConnection發送請求參數。

如果既要使用輸入流讀取URLConnection響應的內容,也要使用輸出流發送請求參數,一定要先使用輸出流,再使用輸入流。

getHeaderField方法用於根據響應頭字段來返回對應的值。而某些頭字段由於經常需要訪問,所以Java提供以下方法來訪問特定響應頭字段的值:

getContentEncoding:獲取content-encoding響應頭字段的值。
getContentLength:獲取content-length響應頭字段的值。
getContentType:獲取content-type響應頭字段的值。
getDate():獲取date響應頭字段的值。
getExpiration():獲取expires響應頭字段的值。
getLastModified():獲取last-modified響應頭字段的值。

下面程序示範瞭如何向Web站點發送GET請求、POST請求,並從Web站點取得響應的示例。

程序清單:codes/17/17-2/TestGetPost.java

public class TestGetPost
{
/**
* 向指定URL發送GET方法的請求
* @param url 發送請求的URL
* @param param 請求參數,請求參數應該是name1=value1&name2=value2的形式。
* @return URL所代表遠程資源的響應
*/
public static String sendGet(String url , String param)
{
String result = "";
BufferedReader in = null;
try
{
String urlName = url + "?" + param;
URL realUrl = new URL(urlName);
//打開和URL之間的連接
URLConnection conn = realUrl.openConnection();
//設置通用的請求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
//建立實際的連接
conn.connect();
//獲取所有響應頭字段
Map<String, List<String>> map = conn.getHeaderFields();
//遍歷所有的響應頭字段
for (String key : map.keySet())
{
System.out.println(key + "--->" + map.get(key));
}
//定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine())!= null)
{
esult += "\n" + line;
}
}
catch(Exception e)
{
System.out.println("發送GET請求出現異常!" + e);
e.printStackTrace();
}
//使用finally塊來關閉輸入流
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result;
}
/**
* 向指定URL發送POST方法的請求
* @param url 發送請求的URL
* @param param 請求參數,請求參數應該是name1=value1&name2=value2的形式。
* @return URL所代表遠程資源的響應
*/ 
public static String sendPost(String url,String param)
{
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try
{
URL realUrl = new URL(url);
//打開和URL之間的連接
URLConnection conn = realUrl.openConnection();
//設置通用的請求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
//發送POST請求必須設置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
//獲取URLConnection對象對應的輸出流
out = new PrintWriter(conn.getOutputStream());
//發送請求參數
out.print(param);
//flush輸出流的緩衝
out.flush();
//定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine())!= null)
{
result += "\n" + line;
}
}
catch(Exception e)
{
System.out.println("發送POST請求出現異常!" + e);
e.printStackTrace();
}
//使用finally塊來關閉輸出流、輸入流
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result;
}
//提供主方法,測試發送GET請求和POST請求
public static void main(String args[])
{
//發送GET請求
String s = TestGetPost.sendGet("http://localhost:8888/abc/
login.jsp",null);
System.out.println(s);
//發送POST請求
String s1 = TestGetPost.sendPost("http://localhost:8888/abc/a.jsp",
"user=李剛&pass=abc");
System.out.println(s1);
}
}

上面程序中發送GET請求時只需將請求參數放在URL字符串之後,以?隔開,程序直接調用URLConnection對象的connect方法即 可,如程序中sendGet方法中粗體字代碼所示;如果程序需要發送POST請求,則需要先設置doIn和doOut兩個請求頭字段的值,再使用 URLConnection對應的輸出流來發送請求參數即可,如程序中sendPost()方法中粗體字代碼所示。

不管是發送GET請求,還是發送POST請求,程序獲取URLConnection響應的方式完全一樣:如果程序可以確定遠程響應是字符流,則可以使用字符流來讀取;如果程序無法確定遠程響應是字符流,則使用字節流讀取即可。

上面程序中發送請求的兩個URL是筆者在本機部署的Web應用,關於如何創建Web應用,編寫JSP頁面請參考筆者所著的《輕量級J2EE企業應用 實戰》。由於程序可以使用這種方式直接向服務器發送請求——相當於提交Web應用中的登錄表單頁,這樣就可以讓程序不斷地變換用戶名、密碼來提交登錄請 求,直到返回登錄成功,這就是所謂的暴力破解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章