zoukankan      html  css  js  c++  java
  • [02] URL和HttpURLConnection类


    1、URL的概念

    统一资源定位符URL(Uniform Resource Locator)是www客户机访问Internet时用来标识资源的名字和地址。

    URL的基本格式是:
    <METHOD>://<HOSTNAME:PORT>/<PATH>/<FILE>  
    • Method是传输协议
    • HOSTNAME是文档和服务器所在的Internet主机名(域名系统中DNS中的点地址)
    • PORT是服务端口号(可省略)
    • PATH是路径名
    • FILE是文件名

    例如:
    http://www.weixueyuan.net/ (http是协议名,www.weixueyuan.net是主机名)
    http://www.weixueyuan.net/view/6079.html (www.weixueyuan.net是主机名,view/6079.html是文件路径和文件名)

    简单的可以把URL理解为包含:协议、主机名、端口、路径、查询字符串和参数等内容。每一段可以独立设置。


    2、URL类

    Java中有个java.net包,其中的类是进行网络编程的,URL对象则是一个可以表示网络资源的类。程序利用URL对象能够实现Internet寻址、网络资源的定位连接、在客户机与服务器之间访问等。

    URL类的构造方法有很多,如下图:
     
    最常用的还是第一种,即 URL(String spec),如 URL url = new URL("http://www.balabala.com:80");

    URL对象的方法也很简单,基本上都是get方法,主要用于分段获取一个URL中各个部分,比单纯地使用一个字符串来表示链接地址,URL对象的方式更加灵活方便。

    同时,它可以使用openConnection方法,获取一个URLConnection对象,以建立网络连接。


    3、HttpURLConnecttion类以及请求发送

    要接收和发送信息还要用HttpURLConnection类,HttpURLConnection类是URLConnection类的子类,其对象往往是通过URL对象进行获取,如下:
    URL url = new URL("http://www.sun.com/");//先要创建一个URL对象
    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();//获得URLConnection对象

    有了URLConnection对象,就可以实现网络连接,主要分三步走:
    • 设置请求头信息和请求体
    • 建立连接
    • 获取响应

    下面以一个模拟发送http请求的方法代码进行示例:
    public static void sendRequest(String link, String RequestMethod, String postContent) {
            System.out.println("============sendRequest start, access link: " + link + " ============");
            try {
                URL url = new URL(link);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    
                //step1:设置请求头信息和请求体
                //设置请求方式
                connection.setRequestMethod(RequestMethod); //GET或POST等
                connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
                connection.setDoInput(true); //是否需要获取输入,默认true
                //设置部分常规的请求头参数
                connection.setRequestProperty("Connection", "Keep-Alive");
                connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
                //设置请求体内容
                if (postContent != null) {
                    OutputStream out = connection.getOutputStream();
                    out.write(postContent.getBytes());
                    out.close();
                }
    
                //step2:建立连接
                //建立连接
                connection.connect();
    
                //step3:获取响应
                //获取响应头
                System.out.println("------------acquire response header start------------");
                Map header = connection.getHeaderFields(); 
                Set<String> keys = header.keySet();
                for (String key : keys) {
                    String val = connection.getHeaderField(key);
                    System.out.println(key + ":" + val);
                }
                System.out.println("------------acquire response header end------------");
                //获取响应体
                System.out.println("------------acquire response body start------------");
                InputStream in = connection.getInputStream(); 
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                byte[] cache = new byte[2048];
                int size = -1;
                while ((size = in.read(cache)) != -1) {
                    out.write(cache, 0, size);
                }
                out.close();
                in.close();
                String responseBody = out.toString();
                System.out.println("response body:" + responseBody);
                System.out.println("------------acquire response body end------------");
                //获取响应码
                int responseCode = connection.getResponseCode(); 
                System.out.println("responseCode:" + responseCode);
    
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("============sendRequest end============");
        }


    4、关于connect()和触发请求发送

    最开始,根据方法名,我以为connect()就是发送请求,并获取到响应,即完成一次网络连接访问。但实际上在写这篇博客的示例代码,在调试时发现,即便不写connect(),也可以完成一次请求模拟,也就是说,connect()不是用来触发发出请求并获取响应的。

    那么何时触发请求的发送呢?我利用IDE的断点和Fiddler的断点调试功能进行了测试,发现在执行“获取响应相关内容的方法时”,才会正式发出HTTP请求并返回响应,这里的相关方法包括但不仅限于:
    • connection.getHeaderFields(); //获取响应头属性集合
    • connection.getInputStream(); //获取响应输入流
    • connection.getResponseCode(); //获取响应状态码

    之前看到某网友说,要正确发送请求需要调用方法connection.getResponseCode(),大概原因在此,尽管他的说法并不完全正确。所以,如果你只是发送GET方法,同时也没有主动获取响应相关的内容,那么实际上并不会发出请求。

    如上例代码中,如果把整个step3的代码全部去掉(即不调用"获取响应相关内容的方法”),如下方法试图请求连接,是不会真正发出请求的
    public static void sendRequest(String link, String RequestMethod, String postContent) {
            System.out.println("============sendRequest start, access link: " + link + " ============");
            try {
                URL url = new URL(link);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    
                //step1:设置请求头信息和请求体
                //设置请求方式
                connection.setRequestMethod(RequestMethod); //GET或POST等
                connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
                connection.setDoInput(true); //是否需要获取输入,默认true
                //设置部分常规的请求头参数
                connection.setRequestProperty("Connection", "Keep-Alive");
                connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
                //设置请求体内容
                if (postContent != null) {
                    OutputStream out = connection.getOutputStream();
                    out.write(postContent.getBytes());
                    out.close();
                }
    
                //step2:建立连接
                //建立连接
                connection.connect();
    
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("============sendRequest end============");
        }
    public static void main(String[] args) {
        //为了Fiddler抓包而设置的代理
        System.setProperty("http.proxyHost", "localhost");
        System.setProperty("http.proxyPort", "8888");
    
        //test code
        String link = "http://www.sctywd.com/login.jsp";
        sendRequest(link, "GET", null); //无法发出HTTP请求
    }

    那么connect()有什么作用呢?看下源码中方法的说明:
     
    由以上,我们大概可以看出几个信息:
    • connect()可以多次调用,但第一次之后的调用都会被自动忽略,即只会调用一次
    • 请求信息只能在连接建立之前修改,一旦连接建立,再试图修改请求信息会出现错误
    • 建立在连接之上的一些方法,会隐性地调用connect()

    那么我们测试一下,已经调用connect,但是在正式发送请求之前,我尝试去修改请求头的配置,结果如之上描述的抛出了异常:
    connection.connect();
    connection.setRequestMethod("GET");//抛出异常 java.net.ProtocolException: Can't reset method: already connected
     
    而且之上也提到过,依赖于连接建立的一些方法如 getOutputStream 和 getInputStream 等,其内部都会隐式的调用 connect(),也就是说,一旦这些方法调用之后,同样再试图更改请求头信息,也是不可以的

    引用某网友: “connect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前,就必须把所有的配置准备好”。

    最后,connect既会隐性地调用,又会因为顺序可能抛出异常,我们要不要主动显式地调用呢,个人建议主动调用,明确顺序流程,避免埋藏一些隐性的bug。

    5、其他

    HttpURLConnection是JDK自带的网络请求工具,定义在java.net包下,当然,类似的还有HttpsURLConnection。实际上在Java中模拟网络连接,还有Apache的HttpClient和Square公司的OkHttp,此处仅作抛砖,供未来和他人引玉了。


    6、参考链接



  • 相关阅读:
    155. 最小栈
    160. 相交链表
    PAT 1057 Stack
    PAT 1026 Table Tennis
    PAT 1017 Queueing at Bank
    PAT 1014 Waiting in Line
    PAT 1029 Median
    PAT 1016 Phone Bills
    PAT 1010 Radix
    PAT 1122 Hamiltonian Cycle
  • 原文地址:https://www.cnblogs.com/deng-cc/p/8395377.html
Copyright © 2011-2022 走看看