Java网络编程-URI和URL

前提

前面的一篇文章《Java中的Internet查询》分析完了如何经过IP地址或者主机名肯定主机在因特网中的地址。任意给定主机上可能会有任意多个资源,这些资源须要有标识符方便主机之间访问对方的资源,所以这篇文章深刻分析一下URL和URI。html

URI

URI全称是Uniform Resource Identifier,也就是统一资源标识符,它是一种采用特定的语法标识一个资源的字符串表示。URI所标识的资源多是服务器上的一个文件,也多是一个邮件地址、图书、主机名等。简单记为:URI是标识一个资源的字符串(这里先没必要纠结标识的目标资源究竟是什么,由于使用者通常不会见到资源的实体),从服务器接收到的只是资源的一种字节表示(二进制序列,从网络流中读取)。URI的语法构成是:一个模式和一个模式特定部分。表示形式以下:java

模式:模式特定部分

scheme:scheme specific part

注意:模式特定部分的表示形式取决于所使用的模式。URI当前的经常使用模式包括:git

  • data:连接中直接包含通过BASE64编码的数据。
  • file:本地磁盘上的文件。
  • ftp:FTP服务器。
  • http:使用超文本传输协议。
  • mailto:电子邮件的地址。
  • magnet:能够经过对等网络(端对端P2P,如BitTorrent)下载的资源。
  • telnet:基于Telnet的服务的链接。
  • urn:统一资源名(Uniform Resource Name)。

除此以外,Java中还大量使用了一些非标准的定制模式,如rmi、jar、jndi、doc、jdbc等,这些非标准的模式分别用于实现各类不一样的用途。github

URI中的模式特定部分没有固定的语法,不过,不少时候都是采用一种层次结构形式,如:apache

//受权机构/路径?查询参数

//authority/path?query

URI的authority部分指定了负责解析该URI其余部分的受权机构(authority),不少时候,URI都是使用Internet主机做为受权机构。例如http://www.baidu.com/s?ie=utf-8,受权机构是www.baidu.com(在URL角度来看,主机名是www.baidu.com)。编程

URI的路径(path)是受权机构用来肯定所标识资源的字符串。不一样的受权机构可能会把相同的路径解析后指向不一样的资源。其实这一点很明显,试下你写两个不一样的项目,主页的路径都是/index.html,它们必定是彻底相同的html文档。另外,路径是能够分层的,分层的各个部分使用斜线"/"进行分隔,而"."和".."操做符用于在分层层次结构中的导航(后面这两个操做符可能不多见到,了解便可)。数组

URI的语法

URI的模式的组成部分能够是小写字母、数字、加号、点(.)和连号(-)。典型的URI的其余三个部分(模式特定部分,也就是受权机构、路径和查询参数)分别由ASCII字母组成(也就是字母A-Z、a-z和数字0-9),此外,还可使用标点符号-、_、.、!和~,而定界符(如/、?、&和=)有其预约义的用途。全部的其余字符,包括ASCII中拉丁字母,都须要使用百分号(%)转义,转义后的格式是:%+字符按照UTF-8编码再转成16进制的字符串表示。注意一点,若是前面提到的定界符没有做为定界符使用,也须要进行转义。举个简单的例子,如URI中存在中文字符"木",木字的UTF-8编码为0xE6 0x9C 0xA8,那么它在URI中应该转义为%E6%9C%A8。Jdk中的URLEncoder或者apache-codec中的相关类库提供URI(URL)编码的功能。浏览器

URI中还能够携带用户的口令,由于会有安全漏洞,因此并不常见,这里也不展开分析。缓存

URI类

URI在Java中抽象为java.net.URI类。安全

构造URI实例

URI实例构造方法有不少个:

public URI(String str) throws URISyntaxException

public URI(String scheme,String userInfo, String host, int port,
        String path, String query, String fragment) throws URISyntaxException

public URI(String scheme, String authority,
       String path, String query, String fragment)throws URISyntaxException

public URI(String scheme, String host, String path, String fragment) throws URISyntaxException

public URI(String scheme, String ssp, String fragment) throws URISyntaxException

//静态工厂方法,最终调用的是URI(String str)
public static URI create(String str)

注意的是构造URI实例的时候会检查是否符合URI的语法,不然会抛出URISyntaxException异常。以上的全部方法都是基于URI的模式+模式特定部分或者URI的各个部分构造URI实例,而静态工厂方法public static URI create(String str)主要是屏蔽了非检查型异常URISyntaxException,转化为检查型异常IllegalArgumentException,这样就不须要显式捕获异常。

获取URI的属性

前面也提到,URI引用包括最多三个部分:模式、模式特定部分和片断标识符,通常格式为:

模式:模式特定片断:片断标识符

基于这种语法规范,URI类提供了下面几个方法获取这些属性:

public String getScheme()

public String getRawSchemeSpecificPart()

public String getSchemeSpecificPart()

public String getFragment()

public String getRawFragment()

PS:之因此没有getRawScheme()方法,是由于URI规范中强制规定,全部的模式名称必须由URI规范中合法的ASCII字符组成,也就是模式名称中不容许存在百分号转义。上面的getRawSchemeSpecificPart()是返回原始的模式特定部分,getSchemeSpecificPart()返回了通过解码(decode)的模式特定部分。同理,getRawFragment()返回原始的片断标识符,而getFragment()返回通过解码(decode)的片断标识符。固然,还有其余方法获取URI的基础属性:

//是否绝对URI
public boolean isAbsolute()

//是否不透明的URI,若是isOpaque()返回true,URI是不透明的
//只能获取到模式、模式特定部分和片断标识符,获取不了host、port等
public boolean isOpaque()

public String getAuthority()

public String getRawAuthority()

public String getRawUserInfo()

public String getUserInfo()

public String getHost()

public int getPort()

public String getRawPath()

public String getPath()

public String getRawQuery()

public String getQuery()

PS:isOpaque()方法为true的时候,说明URI是不透明的,不透明的URI没法获取受权机构、路径、端口和查询参数等。另外,上面的获取属性的方法有些方法存在getRawFoo()的对应方法,这些getRawFoo()方法就是获取原始属性的值,若是没有Raw关键字,则返回解码后的字符串值。

解析相对URI

URI中提供了三个方法用于绝对URI和相对URI之间的转换:

public URI resolve(URI uri)

public URI resolve(String str)

public URI relativize(URI uri)

其中,resolve方法是基于绝对URI和相对URI把相对URI补全为绝对URI,例如:

public static void main(String[] args) throws Exception{
    URI absolute = URI.create("http://localhost:8080/index.html");
    URI relative = URI.create("/hello.html");
    URI resolve = absolute.resolve(relative);
    System.out.println(resolve);
}
//控制台输出
http://localhost:8080/hello.html

relativize方法是基于绝对URI和相对URI反解出绝对URI中的相对URI部分,例如:

public static void main(String[] args) throws Exception{
    URI absolute = URI.create("http://localhost:8080/index.html");
    URI relative = URI.create("http://localhost:8080/");
    URI resolve = relative.relativize(absolute);
    System.out.println(resolve);
}

//控制台输出
index.html

URI的比较

URI类实现了Comparable接口,所以URI能够排序。URI的相等性不是基于字符串直接比较,相等的URI在透明性上必须是一致的,例如都是不透明的,其余部分才能够进行比较。URI比较的时候,模式和受权机构是忽略大小写的,其余部分必须区分大小写,用于转义无效字符串的十六进制数字除外。须要转义的字符在转义前和转义以后进行比较,会认为是不一样的URI。

//1.
URI uri1 = URI.create("http://localhost:8000/index.html");
URI uri2 = URI.create("http://LOCALHOST:8000/index.html");
System.out.println(uri1.equals(uri2));
//输出:true

//2.
URI uri3 = URI.create("http://localhost:8000/index/A");
URI uri4 = URI.create("http://LOCALHOST:8000/index/%41");
System.out.println(uri3.equals(uri4));
//输出:false

URI的字符串表示

URI中有两个方法返回其字符串表示:

public String toString()

public String toASCIIString()

toString()方法返回未编码的字符串形式,也就是特殊的字符不使用百分号转义,所以这个方法的返回值不能保证符合URI的语法,尽管它的各个部分是遵循URI的语法规范的。toASCIIString()方法返回通过编码的字符串形式(US-ACSII编码),也就是特殊的字符必定通过了百分号转义。toString()存在的意义是提升URI的可读性,toASCIIString()方法存在的意义是提升URI的可用性。

URL

URL全称是Uniform Resource Location,也就是统一资源位置。实际上,URL就是一种特殊的URI,它除了标识一个资源,还会为资源提供一个特定的网络位置,客户端能够经过它来获取URL对应的资源。

URL所表示的网络资源位置一般包括用于访问服务器的协议(如http、ftp等)、服务器的主机名或者IP地址、以及资源文件在该服务器上的路径。典型的URL例如http://localhost/myProject/index.html,它指示本地服务器的myProject目录下有一个名为index.html的文档,这个文档能够经过http协议访问(实际上,URL不必定是指服务器中的真实的物理路径,由于咱们通常在服务器中部署应用,如Servlet应用,URL访问的极可能是应用的接口,至于最终映射到什么资源能够由应用自身决定)。

URL的语法

URL的语法表示形式为:

protocol://userInfo@host:port/path?query#fragment

协议://用户信息@主机名:端口/路径?查询#片断

protocol:URL中的协议(protocol)是相应于URI中的模式(schema)的另外一个叫法。URL中,协议部分能够是file、ftp、http、https、magnet、telnet或者其余定制协议字符串(可是不包括urn)。

userInfo:URL中的用户信息(userInfo)是服务器的登陆信息,这部分信息是可选的。若是这部分信息存在,通常会包含一个用户名,极少状况下会包含一个口令。实际上URL携带用户信息是不安全的。

port:URL中的端口号(port)是指服务器中应用的运行端口,默认端口为80,此部分信息是可选的(也就若是不指定端口号就使用默认端口80)。

path:URL中的路径(path)用于表示服务器上的一个特定的目录(其实说一个特定的文件也能够),这个特定的目录不必定是物理目录,也有多是逻辑目录。这一点很容易说明,通常不可能把服务器上面的目录直接公开让全部人访问,服务器上面跑的通常是Web(Java的话通常是Servlet)应用,路径指向的实际数据来源甚至很大多是在其余服务器上的MySQL中的查询结果。

query:查询参数(query)通常是一个字符串,它表示URL中向服务器提供的附加参数,通常只使用在http协议的URL中,其中包含了表单数据,来源于用户的输入,表示形式是key1=value1&key2=value2&keyn=valuen。

fragment:片断(fragment)表示远程服务器资源的某个特定的部分。假如服务器资源是一个HTML文档,此片断标识符将制定为该HTML文档的一个锚(Anchor)。若是远程资源是一个XML文档,那么这个片断标识符是一个XPointer。(若是用Markdown写过博客就知道,添加了导航目录以后,片断就是目录将要导航到的目的章节)

相对URL

URL能够告知浏览器一个文档(Document,假设URL对应服务器上的资源统一叫作文档)的大量信息:获取文档所使用的协议、文档所在的主机、文档在该主机中的路径等。文档中可能也存在引用和当前URL相同的URL,所以,在该文档中使用URL的时候并不要求完整地指定每个URL,URL能够继承其父文档的协议、主机名和路径。继承了父文档URL的部分信息的这类不完整的URL称为相对URL(Reletive URL),相反,完整指定全部部分的URL称为绝对URL(Absolute URL)。在相对URL中,缺乏的各个部分与请求该文档的URL对应的部分相同。举个例子,咱们访问本地服务器的一个HTML文档,URL为http://localhost:8080/myProject/index.html,index.html文档中存在一个超连接<a href="login.html">,当咱们点击此超连接的时候,浏览器会从原始URL(http://localhost:8080/myProject/index.html)中截去index.html而后拼接login.html,最后访问http://localhost:8080/myProject/login.html。

若是相对URL以"/"开头,那么它是相对于文档的根目录,而不是当前的文档。举个例子,咱们访问本地服务器的一个HTML文档,URL为http://localhost:8080/myProject/index.html,index.html文档中存在一个超连接<a href="/login.html">,当咱们点击此超连接的时候,浏览器会跳转到http://localhost:8080/login.html。

相对URL有两个显著的优势:

  • 一、减小文档编写量,毕竟能够省略一部分URL内容,不过这个不是重要的优势。
  • 二、重要的优势是:相对URL容许使用多协议来提供一个文档树,例如http和ftp,使用相对URL编写的文档能够从一个网站直接复制或者迁移到另外一个网站而不会破坏文档内部URL连接。

URL类

java.net.URL类(后面直接叫URL)是JDK对URL的统一抽象,它是一个final修饰的类,也就是不容许派生子类。实际上,URL设计的时候采用了策略模式,不一样的协议的处理器就是不一样的策略,而URL类构成了上下文,经过它决定选用何种策略。URL的核心属性包括协议(或者叫模式)、主机名、端口、查询参数字符串和片断标识符(在JDK中被命名为ref)等,每一个属性均可以单独设置。一旦一个URL对象被构造以后,它的全部属性都不能改变,也就是它的实例是线程安全的。

构造URL实例

URL实例的主要构造方法以下:

//基于URL的各个部分构造URL实例,其中file至关于path、query和fragment三个部分组成
public URL(String protocol, String host, int port, String file) throws MalformedURLException

//基于URL的各个部分构造URL实例,其中file至关于path、query和fragment三个部分组成,使用默认端口80
public URL(String protocol, String host, String file) throws MalformedURLException

//基于URL模式构造URL实例
public URL(String spec) throws MalformedURLException

//基于上下文(父)URL和URL模式构造相对URL实例
public URL(URL context, String spec) throws MalformedURLException

下面基于上面几个构造URL实例的方法举几个简单的编码例子:

//1.
//注意file要带斜杆前缀/
URL url = new URL("http", "127.0.0.1", 8080, "/index");
//输出http://127.0.0.1:8080/index
System.out.println(url.toString());

//2.
URL url = new URL("http://127.0.0.1:8080/index");
//输出http://127.0.0.1:8080/index
System.out.println(url.toString());

//3.
URL url = new URL("http", "127.0.0.1", "/index");
//输出http://127.0.0.1/index
System.out.println(url.toString());

//4.
URL context = new URL("http", "127.0.0.1", "/index");
//构造相对URL,保留协议、host、port部分
URL url = new URL(context, "/login");
//输出http://127.0.0.1/login
System.out.println(url);

上面只说到经过URL类去构造对象,其实还有其余方法获取URL实例,例如:

URL systemResource = ClassLoader.getSystemResource(String name)

Enumeration<URL> systemResources = ClassLoader.getSystemResources(String name)

URL resource = UrlMain.class.getResource(String name)

URL resource = UrlMain.class.getClassLoader().getResource(String name)

Enumeration<URL> resources = UrlMain.class.getClassLoader().getResources(String name)

其中,ClassLoader.getSystemResource(String name)ClassLoader.getSystemResources(String name)都是先判断是否存在SystemClassLoader,若存在则使用SystemClassLoader加载资源,不然使用BootstrapClassLoader(BootstrapClassPath)进行资源加载,简单来讲,这两个方法就是使用系统类加载器加载资源,加载资源时候,从类路径中加载资源,例如使用IDEA,则从编译后的/target目录中加载资源,这一点可使用ClassLoader.getSystemResource("")进行验证。其余三个方法Class#getResource(String name)Class#getClassLoader()#getResource(String name)Class#getClassLoader()#getResources(String name)本质上都是基于AppClassLoader进行资源加载,它们加载资源的时候是从当前的Class所在的类路径(包括类的包路径,若是使用IDEA通常也是/target/类所在的包目录)进行加载,例若有一个类club.throwable.Main.class,若是目录club.throwable存在一张图片doge.jpg,则加载图片的时候能够这样子club.throwable.Main.class.getResource("doge.jpg")。值得注意的一点是,若是须要加载的资源是在一个特定目录中,那么Class#getResource(String name)中的name必须以文件路径分隔符开头,例如Window系统中用'/',其余两个基于经过ClassLoader实例直接加载的不须要使用文件路径分隔符开头,这一点能够看Class#getResource(String name)方法中的resolveName(name)方法。

获取URL的属性

URL实例提供几个方法用于获取数据:

public final InputStream openStream() throws java.io.IOException

public URLConnection openConnection() throws java.io.IOException

public URLConnection openConnection(Proxy proxy) throws java.io.IOException

public final Object getContent() throws java.io.IOException

public final Object getContent(Class[] classes) throws java.io.IOException

InputStream openStream()

openStream()方法链接到URL所引用的资源,在客户端和服务器之间完成必要的握手以后,返回一个InputStream实例,用于读取网络流数据。此方法返回的InputStream实例读取到的内容是Http请求体的原始报文(若是使用Http协议的话),所以有多是一个原始文本片断或者是一个二进制的序列(例如图片)。举个例子:

public static void main(String[] args) throws Exception{
        URL url = new URL("https://www.baidu.com");
        InputStream inputStream = url.openStream();
        int c;
        while ((c= inputStream.read()) != -1){
            System.out.print(c);
        }
        inputStream.close();
    }

URLConnection openConnection()

openConnection()openConnection(Proxy proxy)是类似的,只是后者可使用代理。openConnection()方法为指定的URL新建一个socket,而且返回一个URLConnection实例,它表示一个网络资源打开的链接,咱们能够从这个打开的链接获取一个InputStream实例,用于读取网络流数据。若是上面的过程出现调用失败,会抛出一个IOException。

Object getContent()
Object getContent()Object getContent(Class[] classes)是类似的,后者能够经过Class数组指定获取到的URL中的请求体内容转化为对应的类型的实体。Object getContent()其实是调用了URLConnection实例中的getContent方法,通常来讲,不建议使用这两个方法,由于转换的逻辑执行后的结果通常是不合符咱们预想的结果。

获取URL属性

URL的属性获取能够理解为分解URL成URL组成的各个部分,这些部分的信息能够单独获取。前面提到过URL的各个组成部分,这里重复一下,URL分别由下面的几个部分组成:

  • 模式(schema),也称协议(protocol)。
  • 用户信息(UserInfo),这个并不经常使用。
  • 受权机构,通常是"主机名(Host):port"的格式。
  • 路径。
  • 片断标识符,在Java中称为段或者ref。
  • 查询字符串。

URL类中提供对应的方法分别是:

//获取模式(协议)
public String getProtocol()

//获取主机名
public String getHost()

//获取受权机构,通常是host:port的形式
public String getAuthority()

//获取端口号port
public int getPort()

//返回协议的默认端口,如http协议的默认端口号为80,若是没有指定协议的默认端口则返回-1
public int getDefaultPort()

//返回URL字符串中从主机名后的第一个斜杆/一直到片断标识符的#字符以前的全部字符
public String getFile()

//返回的值和getFile()类似,可是不包含查询字符串
public String getPath()

//返回URL的片断标识符部分
public String getRef()

//返回URL的查询字符串
public String getQuery()

//返回URL中的用户信息,不经常使用
public String getUserInfo()

其中须要注意的是getFile()getPath(),举个例子:

URL url = new URL("https://localhost:8080/search?name=doge#anchor-1");
System.out.println(String.format("path=%s", url.getPath()));
System.out.println(String.format("file=%s", url.getFile()));
//控制台输出
path=/search
file=/search?name=doge

比较

URL实例的比较一般是使用equals()hashCode()方法,当且仅当两个URL指向相同的主机、端口和路径上的资源,而且二者的片断标识符和查询字符串都相同的时候,才会认为两个URL是相等的。equals()调用的时候会尝试使用DNS解析主机,此方法有多是一个阻塞的IO操做,会形成比较大的性能消耗,这个时候须要考虑使用缓存,或者把URL转化为URI进行比较。

转换

URL类中有三个经常使用的实例方法用于转换为另外一种形式:

public String toString()

public String toExternalForm()

public URI toURI()

实际上,toString()最终调用的是toExternalForm(),而toExternalForm()方法是使用StringBuilder把URL的各个组成部分拼接,返回的字符串能够方便直接使用在浏览器中。toURI()方法就是把URL实例转化为URI实例。

URL的编码和解码

咱们在作Web项目或者使用POSTMAN的时候常常会看到x-www-form-urlencoded,它是经常使用的媒体类型或者说内容类型(ContentType),使用这种类型的时候,URL中的字符须要进行编码,那么为何须要进行编码呢?这个是历史遗留缘由,由于发明Web或者说Http(s)协议的时候,Unicode编码并未彻底普及,URL中使用的字符必须来自ASCII的一个固定子集,这个固定子集是:

  • 大写字母A-Z。
  • 小写字母a-z。
  • 数字0-9。
  • 标点符号字符-_.!~*'(和,)

其余字符如:/&?@#;$+=%也可使用,可是它们限定于使用在特定的用途,若是这些字符串出如今URL路径或者查询字符串,它们以及路径和查询字符串的内容必须进行编码。

这里有个须要注意的地方:URL编码只针对URL路径和URL路径以后的部分,由于URL规范中规定,路径以前的部分必须知足ASCII固定子集的内容。

URL编码的方法很简单:除了ASCII数字、字母和部分指定的标点符号以外,全部的其余字符都要转化为字节表示,每一个字节要转化为百分号(%)后面加2位十六进制数字。空格是一种特殊的字符,它使用得比较广泛,通常空格能够编码为%20或者加号(+),可是加号自己的编码为%2B。而/#=&和?不做为分隔符的时候,必须进行编码。

解码过程就是上面编码过程的逆向操做,这里不具体展开。

Java中提供两个类java.net.URLEncoder和java.net.URLDecoder分别用于URL的编码和解码,注意须要使用两个参数的静态方法encode(String value,String charset)decode(String value,String charset),单参数的方法已通过期,不建议使用。注意在使用java.net.URLEncoder和java.net.URLDecoder的时候,它们的API不会判断URL的什么部分须要编码和解码,什么部分不须要编码和解码,直接整个URL字符串丢进去编码必定会出现意料以外的结果。举个反面列子:

String url = "http://localhost:9090/index?name=张大doge";
String encode = URLEncoder.encode(url, "UTF-8");
System.out.println(encode);
//输出:http%3A%2F%2Flocalhost%3A9090%2Findex%3Fname%3D%E5%BC%A0%E5%A4%A7doge

实际上,咱们只须要对Path和Path以后的字符进行编码和解码,例如对于URLhttp://localhost:9090/index?name=张大doge,咱们须要编码和解码的部分只有index、name和张大doge这三个部分,其余部分应该保持原样。正确的例子以下:

public static void main(String[] args) throws Exception {
    String raw= "http://localhost:9090/index?name=张大doge";
    String base = raw.substring(raw.lastIndexOf("//"));
    String pathLeft = base.substring(base.lastIndexOf("/") + 1);
    String[] array = pathLeft.split("\\?");
    String path = array[0];
    String query = array[1];
    base = raw.substring(0,raw.lastIndexOf(path));
    path = URLEncoder.encode(path, "UTF-8");
    String[] queryResult = query.split("=");
    String queryKey = URLEncoder.encode(queryResult[0], "UTF-8");
    String queryValue = URLEncoder.encode(queryResult[1], "UTF-8");
    System.out.println(base + path + "?" + queryKey + "=" + queryValue);
}
//输出结果:http://localhost:9090/index?name=%E5%BC%A0%E5%A4%A7doge
//其中UTF-8编码中张的十六进制表示为E5 BC A0,大的十六进制编码为E5 A4 A7

代理(Proxy)

许多系统会经过代理服务器访问Web应用或者其余服务器中的资源,代理服务器接收从本地客户端发出的请求再转发请求到远程服务器,而后远程服务器返回请求的结果到代理服务器,代理服务器接收到结果以后会把结果回传到本地服务器。这样作有两个比较重要的缘由:

  • 一、安全缘由,防止远端的主机了解关于本地网络配置的细节。
  • 二、过滤出站请求,限制浏览一些禁用的网站。

在Java中除了TCP链接使用传输层的SOCKET代理,其余应用层代理都不支持。Java对于SOCKET没有提供禁用代理的选项,可是能够经过下面三个系统属性配置来开启和限制代理:

http.proxyHost:代理服务器的主机名,默认不设置此系统属性。
http.proxyPort:代理服务器的端口号,默认不设置此系统属性。
http.noneProxyHosts:不须要代理访问的主机名,多个用竖线|分隔,默认不设置此系统属性。

举个例子:
System.setProperty("http.proxyHost", "localhost");
System.setProperty("http.proxyPort", 1080);
System.setProperty("http.noneProxyHosts", "www.baidu.com|github.com");

Proxy类

java.net.Proxy类提供了对代理服务器更细粒度的控制,也就是说这个类容许在编程的使用使用不一样的远程服务器做为代理服务器,而不是经过系统属性全局配置代理。Proxy目前支持三种代理类型,分别是:

  • Proxy.Type.DIRECT:直连,也就是不使用代理。
  • Proxy.Type.HTTP:HTTP代理。
  • Proxy.Type.SOCKS:socket的V4或者V5版本的代理。

使用的时候以下:

SocketAddress socketAddress = new InetSocketAddress("localhost", 80);
Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
Socket socket = new Socket(proxy);
//...

ProxySelector类

每一个运行中的Java虚拟机都会存在一个java.net.ProxySelector实例对象,用来肯定不一样链接使用的代理服务器。默认的java.net.ProxySelector的实现是sun.net.spi.DefaultProxySelector的实例,它会检查各类系统属性和URL的协议,再决定若是链接到不一样的远程代理服务器,固然,开发者也能够继承和实现自定义的java.net.ProxySelector,从而能够根据协议、主机、路径日期等其余标准来选择不一样的代理服务器。java.net.ProxySelector的几个核心的抽象方法以下:

//获取默认的ProxySelector实例
public static ProxySelector getDefault()

//设置默认的ProxySelector实例
public static void setDefault(ProxySelector ps)

//经过URI获取可用的代理列表
public abstract List<Proxy> select(URI uri)

//告知ProxySelector不可用的代理和出现的异常
public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe)

若是须要扩展的话,最好加入缓存的功能,缓存可用的Proxy列表,一旦出现Proxy不可用,经过connectFailed进行清理和剔除不可用的代理节点便可。

小结

URL和URI是当前的网络世界或者系统中资源的重要标识符,了解它们的基础知识才能更好地进行网络编程。URI是统一资源标识符,它能够表示任何介质中的资源。而URL是统一资源位置,通常特指因特网中的网络资源位置,使用于Http或者Https协议中。很显然,URI能够表示的范围更大,URI其实是包含了URL。而二者的区别能够参看上面的几个小节。

(c-5-d e-20181003)

相关文章
相关标签/搜索