客户端:
***
*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 = "";// SessionIDURL 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.youkuaiyun.com/pandazxx/archive/2007/06/18/1657109.aspx
2、http://blog.youkuaiyun.com/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 正文内,因此需要设为 trueconnection.setDoOutput( true );// Read from the connection. Default is true.connection.setDoInput( true );// Set the post method. Default is GETconnection.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( 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 IDString 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的例子, 在这里就先为大家介绍一下。
- void getInput(){
- try
- {
- URL url = new URL("http://www.google.cn/");
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setDoInput(true);
- conn.setConnectTimeout(10000);
- conn.setRequestMethod("GET");
- conn.setRequestProperty("accept", "*/*");
- String location = conn.getRequestProperty("location");
- int resCode = conn.getResponseCode();
- conn.connect();
- InputStream stream = conn.getInputStream();
- byte[] data=new byte[102400];
- int length=stream.read(data);
- String str=new String(data,0,length);
- conn.disconnect();
- System.out.println(str);
- stream.close();
- }
- catch(Exception ee)
- {
- System.out.print("ee:"+ee.getMessage());
- }
- }
只是要注意的是配置一个权限,AndroidManifest.xml 中应该加入如下节点。
- < /activity>
- < /application>
- < uses-permission android:name="android.permission.INTERNET">
- < /uses-permission>
- < /manifest>
可以把AndroidManifest.xml open with manifest editor 来编辑 在permissions中add uses-permission,然后再在name中选择Android.permission.INTERNET,然后save就ok了。
Android HttpURLConnection的基本应用就为大家介绍到这里。
17.2.3 使用URL和URLConnection URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象引用,例如对数据库或搜 索引擎的查询。通常情况而言,URL可以由协议名、主机、端口和资源组成。即满足如下格式:
例如如下的URL地址:
JDK中还提供了一个URI(Uniform Resource Identifiers)类,其实例代表一个统一资源标识符,Java的URI不能用于定位任何资源,它的唯一作用就是解析。与此对应的是,URL则包含 一个可打开到达该资源的输入流,因此我们可以将URL理解成URI的特例。 URL类提供了多个构造器用于创建URL对象,一旦获得了URL对象之后,可以调用如下方法来访问该URL对应的资源: String getFile():获取此URL的资源名。 URL对象中前面几个方法都非常容易理解,而该对象提供的openStream()可以读取该URL资源的InputStream,通过该方法可以非常方便地读取远程资源——甚至实现多线程下载。如下程序所示: 程序清单:codes/17/17-2/MutilDown.java
上面程序中定义了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请求头字段的值。 除此之外,还可以使用如下方法来设置或增加通用头字段: setRequestProperty(String key, String value):设置该URLConnection的key请求头字段的值为value。如下代码所示:
addRequestProperty(String key, String value):为该URLConnection的key请求头字段的增加value值,该方法并不会覆盖原请求头字段的值,而是将新值追加到原请求头字段中。 当远程资源可用之后,程序可以使用以下方法用于访问头字段和内容: Object getContent():获取该URLConnection的内容。 如果既要使用输入流读取URLConnection响应的内容,也要使用输出流发送请求参数,一定要先使用输出流,再使用输入流。 getHeaderField方法用于根据响应头字段来返回对应的值。而某些头字段由于经常需要访问,所以Java提供以下方法来访问特定响应头字段的值: getContentEncoding:获取content-encoding响应头字段的值。 下面程序示范了如何向Web站点发送GET请求、POST请求,并从Web站点取得响应的示例。 程序清单:codes/17/17-2/TestGetPost.java
上面程序中发送GET请求时只需将请求参数放在URL字符串之后,以?隔开,程序直接调用URLConnection对象的connect方法即 可,如程序中sendGet方法中粗体字代码所示;如果程序需要发送POST请求,则需要先设置doIn和doOut两个请求头字段的值,再使用 URLConnection对应的输出流来发送请求参数即可,如程序中sendPost()方法中粗体字代码所示。 不管是发送GET请求,还是发送POST请求,程序获取URLConnection响应的方式完全一样:如果程序可以确定远程响应是字符流,则可以使用字符流来读取;如果程序无法确定远程响应是字符流,则使用字节流读取即可。 上面程序中发送请求的两个URL是笔者在本机部署的Web应用,关于如何创建Web应用,编写JSP页面请参考笔者所著的《轻量级J2EE企业应用 实战》。由于程序可以使用这种方式直接向服务器发送请求——相当于提交Web应用中的登录表单页,这样就可以让程序不断地变换用户名、密码来提交登录请 求,直到返回登录成功,这就是所谓的暴力破解。 |