Java系列学习笔记 --- 网络编程(3)URL

目录

一、URL

        1.1、创建URL对象

                ① 使用完整字符串构造URL对象

                ② 由组成部分构造URL对象

                ③ 构造相对URL对象

        1.2、从URL中获取数据

                ① InputStream openStream()方法

                ② URLConnection openConnection()方法

                ③ Object getContent()方法

                ④ 使用URLConnectiond对象获取URL信息

                        Ⅰ. 创建URLConnection对象

                        Ⅱ. 设置HttpURLConnection对象参数

                        Ⅲ. 通过HttpURLConnection写数据与发送数据问题

                Socket通信实现步骤

1.3、分解URL

1.4、判断两个URL是否相等

1.5、比较两个URL


一、URL

        URL是“统一资源定位符”,表示Internet上某一资源的地址。通过URL,开发人员可以访问Internet上的各种资源

        完整的、带有授权部分的资源标志符语法协议://用户名:密码@子域名.域名.顶级域名:端口号/目录/路径/文件名.文件后缀?参数=值#标志”。

        在Java中,java.net.URL类是对统一资源定位符的抽象。它扩展了java.lang.Object,是一个final类,不能对其派生子类。它不依赖于配置不同类型URL的实例,而使用了策略(strategy)设计模式。协议处理器就是策略,URL类构成上下文,通过它来选择不同的策略。

        需要注意的是,URL是不可变的。构造URL对象之后,其字段不再改变,所以它们的线程是安全的。

1.1、创建URL对象

        和InetAddress对象不同,我们可以构造java.net.URL的实例,这是因为IP地址是唯一的,而IP地址下的资源却是海量的。不同构造函数所需的信息有所不同,具体如下所示。

方法描述

URL(String url)

通过给定的URL字符串创建URL

URL(String protocol, String host, String file)
使用指定的协议、主机名、文件名创建URL,端口使用协议的默认端口。

URL(String protocol, String host, int port, String file)

通过给定的参数(协议、主机名、端口号、文件名)创建URL。

URL(URL context, String url)

使用基地址和相对URL创建

        使用那个构造函数取决于你有那些信息,如果试图为一个不存在的URL创建对象,那么这些构造函数都会抛出一个MalformedURLException异常。

        值得注意的是,Java除了验证能否识别URL模式之外,不会对它构造的URL完成任何正确性检查。所以,程序员应该要确保所创建的URL是合法的。

① 使用完整字符串构造URL对象

        上述第一种构造函数URL(String url)只接受一个字符串形式的绝对URL作为唯一的参数。下面准备了一个很简单的程序,用来确定虚拟机支持那些协议。

public static void main(String[] args) {
		// 超文本传输协议
		test("http://www.baidu.com");
		// 安全http
		test("https://www.baidu.com");
		// 文件传输协议
		test("ftp://ibiblio.org/pub/languages/java/javafaq/");
		// 简单邮件传输协议
		test("mailto:[email protected]");
		// 本地文件访问
		test("file:///tec/password");
}
private static void test(String url){
		try{
			URL u = new URL(url);
			System.out.println("支持 "+u.getProtocol());
		}catch(MalformedURLException ex){
			String protocol = url.substring(0,url.indexOf(':'));
			System.out.println("不支持 "+protocol);
		}
}

        输出结果如下所示

支持 http
支持 https
支持 ftp
支持 mailto
支持 file

② 由组成部分构造URL对象

        通过指定协议、主机名和文件来创建URL对象。

        URL(String protocol,String host,String file)

        需要注意的是,file参数应当以斜线开头,包括路径、文件名和可选的片段标识符。下面准备了一个很简单的程序创建了URL对象。

try{
	URL u = new URL("http","www.baidu.com","/index.html#intro");
}catch(MalformedURLException ex){
	System.out.println("该URL不存在");
}

        在默认端口不正确时,我们可以通过显式指定端口来创建URL对象

        URL(String protocol,String host,int port,String file)

        下面准备了一个很简单的程序创建了URL对象。

try{
	URL u = new URL("http","www.baidu.com",8080,"/index.html");
}catch(MalformedURLException ex){
	System.out.println("该URL不存在");
}

 构造相对URL对象

        这个构造函数根据相对URL和基础URL构造一个绝对URL

        URL(URL base,String relative)

        例如,你可能正在解析HTML文档http://www.baidu.com/index.html,然后遇到一个名为login.html的文件连接,但没有进一步限定信息。这时,可以用包含该链接的文档的URL来提供缺少的信息。这个构造函数会计算出新的URL为http://www.baidu.com/login.html。下面准备了一个很简单的程序。

try{
	URL u1 = new URL("http://www.baidu.com/index.html");
	URL u2 = new URL(u1,"login.html");
}catch(MalformedURLException ex){
	System.out.println("该URL不存在");
}

        如果希望循环处理位于同一个目录下的一组文件,这个构造函数特别有用。

1.2、从URL中获取数据

        获取到URL对象之后,我们就可以获取URL所指向文档中所包含的数据。URL类有以下几个方法可以从URL获取数据。

返回值类型

方法及说明

InputStream

openStream()

打开与此连接URL并返回一个InputStream以从该连接读取。

URLConnection

openConnection()

返回一个URLConnection实例,该实例表示与该引用的远程对象的连接URL

URLConnection

openConnection(Proxy proxy)

相同openConnection(),不同之处在于连接将通过指定的代理建立;不支持代理的协议处理程序将忽略代理参数并进行正常连接。

Object

getContent()

获取此URL的内容

Object

getContent(Class[] classes)

获取此URL的内容

        这些方法中,最常用的是openStream(),它返回一个inputStream,我们可以从这个流读取数据。如果需要更多地控制下载过程,应当调用openConnection方法,这会返回一个可以配置的URLConnection,再由它得到一个InputStream。最后,通过getContent()方法像URL请求其内容。下面我们介绍每个方法。

① InputStream openStream()方法

        该方法链接到URL所引用的资源,在客户端和服务器之间完成必要的握手,然后返回一个InputStream对象,通过此对象可以使用InputStreamReaderBufferedReader来封装从而读取数据。

//获取URL对象
URL url = new URL("http://www.baidu.com");
//通过URL对象打开资源通道,返回InputStream原始资源字节流
InputStream is = url.openStream();
//InputStreamReader是字节流通向字符流的桥梁(也可以不用)
InputStreamReader isr = new InputStreamReader(is,"utf-8");
//BufferedReader从字符输入流中读取文本
BufferedReader br = new BufferedReader(isr);
String str;
while ((str = br.readLine()) != null) {
    System.out.println(str);
}
//关闭流并释放与之关联的所有资源
br.close();
isr.close();
is.close();

        从上述案例中,首先创建了URL对象,通过URL对象的openStream()方法打开输入流获取InputStream对象,再由此对象创建BufferedReader对象,通过此对象获取URL所指定资源文件的数据。

        需要注意的是,从这个InputStream获取的数据是URL引用的原始数据(即未经解释的内容):如果读取ASCII文件,文件则为ASCII;如果读取HTML文件,则为原始HTML;如果读取图像文件,则为二进制图片数据等。它不包括任何HTTP首部或与协议有关的任何其他信息。

② URLConnection openConnection()方法

        URL对象使用OpenConnection()方法打开指定的URL的Socket链接,并返回一个URLConnection对象。该对象表示URL所引用的远程对象的连接,然后再通过该对象的connect()方法进行链接。简单的范例代码如下所示。

//获取URL对象
URL url = new URL("http://www.baidu.com");
//调用URL对象openConnection()方法,获取URLConnection对象
URLConnection URLconnection = url.openConnection();
//将URLConnection对象转换成HttpURLConnection对象
HttpURLConnection httpConnection = (HttpURLConnection)URLconnection;
//获取请求响应码,判断访问是否成功
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
    System.err.println("成功");   
    InputStream is = httpConnection.getInputStream();
    InputStreamReader isr = new InputStreamReader(is,"utf-8");
    BufferedReader bufr = new BufferedReader(isr);
    String str;
    while ((str = bufr.readLine()) != null) {
        System.out.println(str);
    }
    bufr.close();
    isr.close();
    is.close();
} else {
    System.err.println("失败");
}

        如果希望与服务器直接通信,应当使用这个方法。通过URLConnection对象,可以访问服务器发送的所有数据;除了原始的文档本身之外(如HTML、纯文本、二进制图像数据等),它还允许你向URL写入数据,还可以访问这个协议指定的所有元数据。例如,访问HTTP首部以及原始HTML。

 Object getContent()方法

        getContent()方法获取由URL引用的数据,尝试由它建立某种类型的对象。如果URL指示某种文本(如ASCII或HTML文件),返回的文件通常是InputStream;如果URL指示一个图像,则返回一个java.awt.ImageProducer。它们本身并不是数据对象,而是一种途径,程序可以利用它们构造数据对象。

        getContent()的做法是,从服务器获取的数据首部中查找Content-type字段。如果服务器没有使用MIME首部,或者发送了一个不熟悉的Content-type,getContent()会返回某种InputStream对象,如果无法获取这个对象,就会抛出一个IOException异常。

④ 使用URLConnectiond对象获取URL信息

Ⅰ. 创建URLConnection对象

URL url = new URL("http://www.baidu.com"); 
// HTTP协议生成的URLConnection类可以将其转换为HttpURLConnection子类,以便用到HttpURLConnection更多的API
URLConnection urlConnt = url.openConnection();
HttpURLConnection httpUrlConnt = (HttpURLConnection) urlConnt;

Ⅱ. 设置HttpURLConnection对象参数

// 设置是否向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", "text/html");
// 设定请求的方法为"POST",默认是GET
httpUrlConnection.setRequestMethod("POST");
// 连接,从上述第2条中url.openConnection()至此的配置必须要在connect之前完成,
httpUrlConnection.connect();

. 通过HttpURLConnection写数据与发送数据问题

// 现在通过“输出流对象”构建“对象输出流对象”,以实现输出可序列化的对象。 
ObjectOutputStream objOutputStrm = new ObjectOutputStream(outStream);
// 向对象输出流写出数据,这些数据将存到内存缓冲区中 
objOutputStrm.writeObject(new String("我是测试数据"));
// 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream) 
objOutputStm.flush();
// 关闭流对象。此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区
// 在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器 
objOutputStm.close();

// 调用HttpURLConnection连接对象的getInputStream()函数, 
// 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。 
InputStream inStrm = httpUrlConnt.getInputStream();
// 上边的httpConn.getInputStream()方法已调用,本次HTTP请求已结束,下边向对象输出流的输出已无意义, 
// 既使对象输出流没有调用close()方法,下边的操作也不会向对象输出流写入任何数据. 
// 因此,要重新发送数据时需要重新创建连接、重新设参数、重新创建流对象、重新写数据、 
// 重新发送数据(至于是否不用重新这些操作需要再研究) 
objOutputStm.writeObject(new String(""));
httpUrlConnt.getInputStream();

Socket通信实现步骤

        1. 创建ServerSocekt和Socket
        2. 打开连接到Socket的输入/输出流
        3. 依照协议对Socket进行读写操作
        4. 关闭输入输出流、关闭Socket

 

1.3、分解URL

        URL由5部分组成:协议、授权机构、路径、查询字符串、片段标识符

        例如,在http://www.baidu.com/index.html?id=220308#toc中,协议是http;授权机构是www.baidu.com;路径是index.html;查询字符串是id=220308;片段标识符是toc。

        授权机构还可以进一步划分为用户信息、主机和端口。例如在http://[email protected]:8080/中,授权机构包含用户信息admin、主机www.baidu.com和端口8080

        java提供了下面9个公共方法对上述URL组成部分进行只读访问

返回类型

描述

描述

String

getProtocal()

返回URL的协议

String

getHost()

返回URL的主机

int

getPort()

返回URL端口部分

String

getFile()

返回URL文件名部分

String

getQuery()

返回URL查询部分。

String

getPath()

返回URL路径部分。

String

getAuthority()

获取此 URL 的授权部分。

int

getDefaultPort()

协议的默认端口号。

String

getRef()

返回URL片段标识符

String

getUserInfo()

返回用户名或口令信息。

        下面通过简单程序演示上面的这个方法

try{
    URL url = new URL("http://www.baidu.com/index.html?id=220308#Laoye");
    System.out.println("URL 为:" + url.toString());
    System.out.println("协议为:" + url.getProtocol());
    System.out.println("验证信息:" + url.getAuthority());
    System.out.println("文件名及请求参数:" + url.getFile());
    System.out.println("主机名:" + url.getHost());
    System.out.println("路径:" + url.getPath());
    System.out.println("端口:" + url.getPort());
    System.out.println("默认端口:" + url.getDefaultPort());
    System.out.println("请求参数:" + url.getQuery());
    System.out.println("定位位置:" + url.getRef());
}catch(IOException e){
	 e.printStackTrace();
}

        输出结果如下所示。

[URL: http://www.baidu.com/index.html?id=220308#Laoye]
[协议: http]
[验证信息: www.baidu.com]
[文件名及参数: /index.html?id=220308]
[主机名: www.baidu.com]
[资源路径: /index.html]
[端口: -1]
[默认端口: 80]
[请求参数: id=220308]
[片段标识符: Laoye]

1.4、判断两个URL是否相等

        URL类包含通常的equals()和hashCode()方法,当且仅当两个URL都指向相同主机、端口和路径上的相同资源,而且具有相同的片段标识符和查询字符串,才认为这两个URL是相等。实际上equals()方法会尝试使用DNS解析主机,来判断两个主机是否相同。

        另一方面,equals()还不够深入,不会具体比较两个URL标识的资源。例如,http://www.baidu.com/不等于http://www.baidu.com/index.html。

        下面我们通过简单的范例程序来演示。

URL url1 = new URL("http://www.baidu.com");
URL url2 = new URL("http://baidu.com");
if(url1.equals(url2)){
	System.out.println("URL1 和 URL2 相等");
}else{
	System.out.println("URL1 和 URL2 不相等");
}

        除此之外,URL类还有一个sanmeFile()方法,可以检查两个URL是否指向相同的资源。

try{
	URL url1 = new URL("http://www.baidu.com:80/index.html");
	URL url2 = new URL("http://www.baidu.com/index.html");
	if(url1.sameFile(url2)){
		System.out.println("URL1 和 URL2 指向相同的资源");
	}else{
		System.out.println("URL1 和 URL2 指向不同的资源");
	}
}catch(MalformedURLException ex){
	System.err.println(ex);
}

        需要注意的是,sameFile()只有在两个URL路径几乎一样才会返回true,即使前面的请求路径完全一样,但是请求参数不一样也会返回false,例如下面范例程序所示的情况。

URL url1 = new URL("http://www.baidu.com/index.html");
URL url2 = new URL("http://baidu.com/index.html");
URL url3 = new URL("http://www.baidu.com/index.html?id=308");
URL url4 = new URL("http://www.baidu.com/index.html?id=309");

        上述的4个对象两两互不相等。

1.5、比较两个URL

        URL有3个方法可以将一个实例转换为另外一种形式,分别是toString()、toExternalForm()和toURI()。

        toString()返回的是String类型的绝对URL。显示调用toString()并不常见,通过显示(打印)语句会隐式调用toString()方法,除了打印语句以外,使用toExternalForm()方法更合适。

        toExternalForm()方法将一个URL对象转换为一个字符串(拼接方式)。该方法返回表示这个URL的刻度String,它等同于toString()方法。事实上,URL的toString()方法就是返回该方法。

        最后,toURI()方法将URL对象转换为URI对象。值得注意的是,URI类提供了比URL类更精确、更符合规范的行为。对于像绝对化和编码等操作,在选择时应当首选URI类。如果需要把URL存储在一个散列表或其他数据结构中,也应当首选URI类,因为它equals()方法不会阻塞。 ⬅ 重点理解

猜你喜欢

转载自blog.csdn.net/Rao_Limon/article/details/89351485