import java.net.Proxy; import java.net.ProxySelector; import java.net.SocketAddress; import java.net.InetSocketAddress; import java.net.URI;
1. 代理服务器:
1) 英文就叫Proxy,即代替用户去连接想要的网站并获取信息;
2) 一般主流的商业浏览器都提供代理的功能,即你可以先设置一个代理服务器,那么接下来所有的上网都是通过该指定的代理服务器完成;
3) 一旦设置好了代理服务器,那么你之后所有的上网行为都是通过代理服务器完成的:
i. 你输入一个网址并回车后,会先连接代理服务器;
ii. 然后代理服务器帮你打开那个网址并获取你想要的信息,然后将信息返回给你;
iii. 即代理服务器帮你上网,帮你下载,最后将结果返回给你;
4) 为什么需要代理服务器:
i. 处于特殊安全原因需要对外隐藏自己的IP,代理服务器对外只能暴露代理服务器本身的IP,但是可以隐藏你的IP;
ii. 访问国内特定单位、内部团体的资料(也是i.的一种特殊情况);
iii. 代理服务器可以提供缓存,大大提高访问速度(即你访问一个资源的时候,如果之前有其他人通过Proxy访问过了,那么Porxy就会把该资源直接放入自己的缓存中,下次再有用户想要获取就直接从缓存中拿而不是去连接这个远程资源了;
e.g. 迅雷下载里面的离线下载/取回本地,之所以速度那么快是因为迅雷服务器已经将该资源保存在服务器的缓存中了,你下载的时候并不是直接连接远程资源,而是直接从迅雷服务器取回本地,而迅雷可以提供最大带宽接入的服务;
e.g. 搜索引擎提供的网页,你直接打开词条进入网页其实进入的并不是远程连接的网页,搜索引擎将这些搜集到的网页暂时保存在自己的服务器里,你其实是直接从搜索引擎服务器的数据库中下载到这些网页,因此打开以及搜索特别快速,就是这个原因;
PS: 只不过搜索引擎提供一种机制,它可以保证真正的远程网页一旦被更新它数据库中保存的网页也可以及时地同步更新;
Proxy
1) Java中就用Proxy类来代表代理服务器;
2) 在URL的openConnection就有一个版本,其参数就是Proxy对象,包括Socket等的构造器也都有传入Proxy对象的重载版本,一旦调用了以上方法指定了代理服务器,那么接下来的所有对外访问行为都是通过指定的代理服务器进行的;
3) Proxy构造器:Proxy(Proxy.Type type, SocketAddress sa);
i. Proxy.Type是Proxy类中定义的枚举类型,共有三个值:DIRECT(直连,不使用代理)、HTTP(高级协议代理,HTTP、FTP等的代理)、SOCKS(SOCKS V4、V5的代理);
ii. 之前的openConnection以及不带Proxy参数的Socket构造器等都是默认DIRECT直连的,即不使用任何代理的直连;
iii. sa即代理服务器的IP地址/端口号;
4) openConnection时指定代理:URLConnection URL.openConnection(Proxy proxy); // 用指定的代理服务器代开连接
5) 指定socket使用代理服务器:Socket(Proxy proxy);
6) 示例:openConnection使用代理
public class Test {
// 代理服务器的IP和端口
private final String PROXY_ADDR = "129.82.12.188";
private final int PROXY_PORT = 3124;
public void init() throws IOException {
URL url = new URL("http://www.baidu.com"); // 想访问的网址
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_ADDR, PROXY_PORT));
URLConnection conn = url.openConnection(proxy); // 连接时设置代理
conn.setConnectTimeout(3000);
try(
Scanner scan = new Scanner(conn.getInputStream());
PrintStream ps = new PrintStream("index.htm")) {
while (scan.hasNextLine()) { // 直接将返回的网页代码下载到本地的index.htm文件中
String line = scan.nextLine();
System.out.println(line);
ps.println(line);
}
}
}
public static void main(String[] args) throws IOException {
new Test().init();
}
}
ProxySelector——自动代理选择器:
1) 直接显示传入Porxy对象的方法未免有点太繁琐
主要问题: 有时候需要根据不同的网址选择不同的代理服务器,比如访问欧洲的网页时需要用一种代理服务器加速,而访问美洲的网页时可能用另一个代理更快更好,那么每次都要这样显式地指定代理岂不是很繁琐吗?
2) Java提供了一个抽象类ProxySelector,该类的对象可以根据你要连接的URL自动选择最合适的代理,但是该类是抽象类,需要自己继承该类实现个性化、先进化的代理自动选择;
3) 选择器实现完毕后就可以创建选择器的对象实例,并调用ProxySelector类的静态方法setDefault将该选择器设置为默认选择器;那么之后,所有的访问网络的行为(openConnection、Socket构造器)等都不需要指定代理,直接自动交给代理选择器自动根据你要访问的URL选择一个代理帮你访问网络;
4) ProxySelector:其中有两个重要的方法需要实现
i. List<Proxy> select(URI uri); // 给定一个URI(URL的父类,但实际中只用URL)返回一个最适合访问该URI的代理服务器列表,其中会首选列表的第一个代理,如果不行则用第二个,如果全部不行就会调用connectFailed方法处理连接失败问题
ii. void connectFailed(URI uri, SocketAddress sa, IOException ioe);
a. URI是目标网址;
b. sa是连接失败的那个Proxy的IP地址;
c. ioe是连接失败时所抛出的异常对象;
5) 设置默认的代理选择器:
i. 当选择器设计好后就可以创建出选择器对象;
ii. 但是创建完对象后该对象并不会起作用,必须要将其设为本程序的默认选择器才会使其生效(运作);
iii. 直接调用ProxySelector的静态方法(本质是一个框架函数)setDefault将该选择器注册给框架(即设定为默认选择器);
iv. 之后选择器就生效了,并开始运作;
v. 方法原型:static void ProxySelector.setDefault(ProxySelector ps);
public class Test {
private final String PROXY_ADDR = "139.82.12.188";
private final int PROXY_PORT = 3124;
public void init() {
ProxySelector.setDefault(new ProxySelector() {
@Override
public List<Proxy> select(URI uri) { // 默认任何URI都返回一个代理
// TODO Auto-generated method stub
// return null;
List<Proxy> list = new ArrayList<>();
list.add(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_ADDR, PROXY_PORT)));
return list;
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { // 简单地打印信息
// TODO Auto-generated method stub
System.out.println("无法连接到代理!");
}
});
// ... 正常的访问网络
}
public static void main(String[] args) throws IOException {
new Test().init();
}
}
系统默认的代理选择器:
1) 每个Java程序启动时都会得到一个跟该程序有关的系统属性表(可以看做是该程序独有的环境变量);
2) 该属性表中记录有该Java程序访问网络默认使用的代理(http代理、ftp代理、socks代理等),只不过初始时全部都是空的(即默认所有协议都采用直连,即无代理);
3) 如果自己设计并设置代理选择器,其实Java也提供了一个默认实现的代理选择器,即sun.net.spi.DefaultProxySelector,自己未设置之前使用的就是这个Sun公司提供的默认代理选择器,而该选择器的select方法的实现策略就是就是根据给定的URI返回系统属性表中指定的代理,只不过初始时属性表中的代理都是为空,因此默认采取的是直连而已;
4) sun.net.spi.DefaultProxySelector虽然存在并且运行着,但不过该API没有公开,目的就是防止用户显示使用它造成不安全因素,因此如果你想改变默认选择器对代理的选择只能通过修改系统属性表的方式得到想要的代理;
5) 系统属性表中的代理属性:
i. 代理的属性名格式为“协议名.XXX”,协议支持http、https、ftp、socks等;
ii. XXX是代理的信息,主要有3种:
a. proxyHost:代理的IP地址
b. proxyPort:代理服务器的端口
c. nonProxyHosts:不需要使用代理就可以访问的网址(主机),可以指定多个,多个网址之间用|隔开,允许使用通配符*表示
iii. 一条典型的代理属性(键值对):
http.proxyHost: 192.168.10.96 // 该http代理服务器的IP地址是192.168.10.96
ftp.nonProxyHosts: localhost | 10.10.10.* // localhost(表示本机)以及所有以10.10.10开头的ftp地址访问是无需代理,直连就行
6) 设置系统属性:
i. 必须要先获得系统属性表对象:static Properties System.getProperties();
ii. 设置属性值:synchronized Object Properties.setProperty(String key, String value); // 返回的是修改前的value,如果修改前没有value则返回null
e.g. 可以看到键值都是字符串类型的;
7) 示例:Properties props = System.getProperties(); props.setProperty("socks.proxyHost", "192.168.10.6");
8) 使用ProxySelector的静态方法getDefault获得当前正在使用的代理选择器:static ProxySelector ProxySelector.getDefault();
e.g. 如果设置过自己设计的选择器则返回的就是自己设计的那个,如果什么都没动就能返回Sun公司没有公开的sun.net.spi.DefaultProxySelector咯!
9) 另外一个sun.net.spi.DefaultProxySelector默认选择器的实现方法connectFailed,其实现策略就是直连,即如果用代理连接失败的话(比如等了很长时间还是连不上)就不用代理则直连目标网址;
转载自 https://blog.csdn.net/Lirx_Tech/article/details/51005281