1、URL的概念

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

URL的基本格式是:
<METHOD>://<HOSTNAME:PORT>/<PATH>/<FILE>  
1
 
1
<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对象
2
 
1
URL url = new URL("http://www.sun.com/");//先要创建一个URL对象
2
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============");
}
60
 
1
public static void sendRequest(String link, String RequestMethod, String postContent) {
2
        System.out.println("============sendRequest start, access link: " + link + " ============");
3
        try {
4
            URL url = new URL(link);
5
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
6

7
            //step1:设置请求头信息和请求体
8
            //设置请求方式
9
            connection.setRequestMethod(RequestMethod); //GET或POST等
10
            connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
11
            connection.setDoInput(true); //是否需要获取输入,默认true
12
            //设置部分常规的请求头参数
13
            connection.setRequestProperty("Connection", "Keep-Alive");
14
            connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
15
            //设置请求体内容
16
            if (postContent != null) {
17
                OutputStream out = connection.getOutputStream();
18
                out.write(postContent.getBytes());
19
                out.close();
20
            }
21

22
            //step2:建立连接
23
            //建立连接
24
            connection.connect();
25

26
            //step3:获取响应
27
            //获取响应头
28
            System.out.println("------------acquire response header start------------");
29
            Map header = connection.getHeaderFields(); 
30
            Set<String> keys = header.keySet();
31
            for (String key : keys) {
32
                String val = connection.getHeaderField(key);
33
                System.out.println(key + ":" + val);
34
            }
35
            System.out.println("------------acquire response header end------------");
36
            //获取响应体
37
            System.out.println("------------acquire response body start------------");
38
            InputStream in = connection.getInputStream(); 
39
            ByteArrayOutputStream out = new ByteArrayOutputStream();
40
            byte[] cache = new byte[2048];
41
            int size = -1;
42
            while ((size = in.read(cache)) != -1) {
43
                out.write(cache, 0, size);
44
            }
45
            out.close();
46
            in.close();
47
            String responseBody = out.toString();
48
            System.out.println("response body:" + responseBody);
49
            System.out.println("------------acquire response body end------------");
50
            //获取响应码
51
            int responseCode = connection.getResponseCode(); 
52
            System.out.println("responseCode:" + responseCode);
53

54
        } catch (MalformedURLException e) {
55
            e.printStackTrace();
56
        } catch (IOException e) {
57
            e.printStackTrace();
58
        }
59
        System.out.println("============sendRequest end============");
60
    }


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============");
}
32
 
1
public static void sendRequest(String link, String RequestMethod, String postContent) {
2
        System.out.println("============sendRequest start, access link: " + link + " ============");
3
        try {
4
            URL url = new URL(link);
5
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
6

7
            //step1:设置请求头信息和请求体
8
            //设置请求方式
9
            connection.setRequestMethod(RequestMethod); //GET或POST等
10
            connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
11
            connection.setDoInput(true); //是否需要获取输入,默认true
12
            //设置部分常规的请求头参数
13
            connection.setRequestProperty("Connection", "Keep-Alive");
14
            connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
15
            //设置请求体内容
16
            if (postContent != null) {
17
                OutputStream out = connection.getOutputStream();
18
                out.write(postContent.getBytes());
19
                out.close();
20
            }
21

22
            //step2:建立连接
23
            //建立连接
24
            connection.connect();
25

26
        } catch (MalformedURLException e) {
27
            e.printStackTrace();
28
        } catch (IOException e) {
29
            e.printStackTrace();
30
        }
31
        System.out.println("============sendRequest end============");
32
    }
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请求
}
x
 
1
public static void main(String[] args) {
2
    //为了Fiddler抓包而设置的代理
3
    System.setProperty("http.proxyHost", "localhost");
4
    System.setProperty("http.proxyPort", "8888");
5

6
    //test code
7
    String link = "http://www.sctywd.com/login.jsp";
8
    sendRequest(link, "GET", null); //无法发出HTTP请求
9
}

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

那么我们测试一下,已经调用connect,但是在正式发送请求之前,我尝试去修改请求头的配置,结果如之上描述的抛出了异常:
connection.connect();
connection.setRequestMethod("GET");//抛出异常 java.net.ProtocolException: Can't reset method: already connected
 
1
connection.connect();
2
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、参考链接



[02] URL和HttpURLConnection类的更多相关文章

  1. HttpURLConnection类

    导语 java.net.HttpURLConnectin类是URLConnection类的抽象子类.它在处理协议为HTTP的URL时特别有效.具体而言,它通过它可以获取和设置请求方法,确定是否重定向, ...

  2. JDK下sun.net.www.protocol.http.HttpURLConnection类-----Http客户端实现类的实现分析

    HttpClient类是进行TCP连接的实现类, package sun.net.www.http; import java.io.*; import java.net.*; import java. ...

  3. 一个用php实现的获取URL信息的类

    获取URL信息的类 使用这个类,你能获得URL的如下信息: - Host  - Path  - Statuscode (eg. 404,200, ...)  - HTTP Version  - Ser ...

  4. HttpURLConnection类的使用

    此类以获取天气的一个api地址为例: package javaexcjs; import java.io.BufferedReader; import java.io.OutputStreamWrit ...

  5. url提交参数类

    url提交参数类 type /// <summary> /// 准备url /// </summary> TynUrl = class private FUrl, FComma ...

  6. UrlUtils工具类,Java URL工具类,Java URL链接工具类

    UrlUtils工具类,Java URL工具类,Java URL链接工具类 >>>>>>>>>>>>>>>&g ...

  7. Django 02 url路由配置及渲染方式

    Django 02 url路由配置及渲染方式 一.URL #URL #(Uniform Resoure Locator) 统一资源定位符:对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是 ...

  8. HttpURLConnection访问url的工具类

    java代码: import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; ...

  9. Android基于HttpUrlConnection类的文件下载

    /** * get方法的文件下载 * <p> * 特别说明 android中的progressBar是google唯一的做了处理的可以在子线程中更新UI的控件 * * @param pat ...

随机推荐

  1. 洛谷P4344 [SHOI2015]脑洞治疗仪(ODT)

    题意 题目链接 Sol ODT板子题. 操作1直接拆区间就行. #include<bits/stdc++.h> #define fi first #define se second con ...

  2. React 入门学习笔记整理(一)——搭建环境

    使用create-react-app脚手架搭建环境 1.安装node .软件下载地址:https://nodejs.org/en/,我下的推荐的版本. 安装之后测试是否安装成功.windows系统下, ...

  3. 阿里云 centos7 django + uWSGI+Nginx + python3 部署攻略

    centos7+nginx+python3+django+uwsgi配置Django 项目部署   1.租的服务器(选择centos)的话,需要在阿里云后台控制台开放几个端口,克隆一下已开放的端口,t ...

  4. 将mssql数据库高版本迁移到低版本

    将mssql数据库高版本迁移到低版本 在低版本目标数据库中创建目标空数据库[TargetDb] ,注意新建数据库即可,不要创建任何表 在低版本数据库中,选中[服务器对象=>链接服务器] 右键[新 ...

  5. 移动端上拉加载,下拉刷新效果Demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. HTTP协议响应码及get请求和post请求比较

    HTTP协议响应码 1XX:信息响应类,表示接收到请求并且继续处理 2XX:处理成功响应类,表示动作被成功接受.理解和接受 200 OK:表示从客户端发来的请求在服务器端被正常处理了 204 No C ...

  7. 【PAT】B1057 数零壹(20 分)

    简单题,简单字符串处理加简单数学进制转换 #include<stdio.h> #include<string.h> #include<ctype.h> int ma ...

  8. 【PAT】B1082 射击比赛(20 分)

    水提水题,直接贴代码啦 #include<cstdio> #include<algorithm> using namespace std; struct ppp{ int id ...

  9. January 06th, 2018 Week 01st Saturday

    In life the most interesting things tend to happen when you are on your way to do something else. 生活 ...

  10. jsp 一点点

    jsp学习 jsp -处理 作为正常的页面,你的浏览器发送一个http请求道web服务器. web 服务器承认一个JSP页面的HTTP请求,并将其转发给一个JSP引擎. JSP引擎从磁盘加载JSP页面 ...