还是网络

本文深入探讨了使用Java中的HttpURLConnection发起HTTP请求的方法,包括GET和POST请求的详细过程及注意事项,同时介绍了如何处理服务器响应和利用多线程进行高效下载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 客户端:

    ***
    *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.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 正文内,因此需要设为 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( 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应用中的登录表单页,这样就可以让程序不断地变换用户名、密码来提交登录请 求,直到返回登录成功,这就是所谓的暴力破解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值