Java Networking and Proxies(译文)



JavaNetworking and Proxies

比较早的文章,正好在研究java proxy的用法,就翻译了一下

原文地址:

http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html

  1. 概述

    在如今的网络环境下,尤其是合作项目,项目开发者不得不频繁的处理代理问题。

    有时候项目使用系统默认参数即可,但有些情况下,希望能够详密掌控代理的使用情况。

    很多项目会提供用户GUI来自己设定代理参数。

    无论如何,一个开发平台,比如java,应该提供灵活强大的机制来处理代理问题。Javase5.0处理了这类问题

  2. 系统特性

    在JavaSe1.4中,只能通过systemproperties去设置代理服务器。复杂一点的情况下,这些参数的名字已经被改来改去,面目全非。

    使用systemproperties有个很大的限制。就是一旦代理设定了某个协议,那么所有的链接都将遵循这个协议。非黑即白,无法自由设置。这是机械化的做法,一点都不人性化。

    有两个方法设置systemproperties

  1. 借助虚拟机使用命令行
  2. 使用System.setProperty(String,String)方法

所有的代理被定义为一个主机名+一个端口号。

2.1 HTTP

有三个properties可以指定HTTP协议的proxy

http.proxyHost

http.proxyPort:默认值80

http.nonProxyHosts:一些将不会用代理服务器链接的地址,用“|”分割

我们来看一些例子:

例一:

$ java-Dhttp.proxyHost=webcache.example.com GetURL

所有http链接都将通过“webcache.example.com”这个代理服务器的80端口

例二:

$ java -Dhttp.proxyHost=webcache.example.com-Dhttp.proxyPort=8080

-Dhttp.nonProxyHosts=”localhost|host.example.com” GetURL

在第二个例子中,代理服务器还是“webcache.example.com”,但是端口变成了8080,并且当链接localhost或者是host.mydonain.com时将不会使用代理服务器。

之前提到的,这些设定会影响所有的HTTP链接。

当然,我们可以使用代码让这个设定变得轻巧,动态:

   //Set the http proxy to webcache.example.com:8080

System.setProperty("http.proxyHost","webcache.example.com");

System.setProperty("http.proxyPort","8080");

// Next connection will be through proxy.

URL url = new URL("http://java.example.org/");

InputStream in = url.openStream();

// Now, let‘s ‘unset‘ the proxy.

System.setProperty("http.proxyHost", null);

// From now on http connections will be done directly.

即便是有些繁琐,代码工作正常,但是在多线程的应用中会变得有些微妙。记住,systemproperties是虚拟机的参数,所以所有的线程都会被影响到。

2.2) HTTPS

htttps.proxyHost:

https.proxyPort:默认443

2.3) FTP

ftp.proxHost

ftp.proxyPort:默认80

ftp.nonProxyHosts

 

2.4) SOCKS

SOCKS协议,在RFC1928中定义,提供一个框架给C/S应用,用于TCP/UDP层(传输层)安全的穿过防火墙。这个比高层协议比如HTTP或者FTP(应用层)更通用

socksProxyHost

socksProxyPort:默认1080

如果同时设定了SOCKS和HTTP代理怎么办?

原则是优先设定更高层协议,比如HTTP或者FTP将会有更高的优先权。

例子:同时设定HTTP和SOCKS代理:

$ java -Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
-DsocksProxyHost=socks.example.com GetURL

HTTPURL-》HTTPProxy

FTP-》SOCKSProxy

3)   Proxy类

system properties很有用,但是不灵活。非黑即白的方式对于开发者来说是一个非常苛刻的限制。所以一个灵活的API将满足这个问题。

这个API就是Proxy类,定义了代理的3种类型:

  1. DIRECT:不使用代理
  2. HTTP:使用HTTP协议的代理
  3. SOCKS:使用SOCKS v4或者v5协议的代理

    所以,建立一个HTTP代理将用以下方法:

SocketAddress addr = new
InetSocketAddress("webcache.example.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);

记住!这个Proxy实例只是代表着一个代理的定义。

怎么使用呢?

URL url = new URL("http://java.example.org/");
URConnection conn = url.openConnection(proxy);

同样的机制可以指定一个特殊的链接不使用代理,用这种方式不用定义Proxy的实例。

URL url2 = new URL("http://infos.example.com/");

URLConnection conn2 =url2.openConnection(Proxy.NO_PROXY);

一样的,可以用SOCKS代理进行连接,方式也一样

SocketAddress addr = new InetSocketAddress("socks.example.com", 1080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
URL url = new URL("ftp://ftp.gnu.org/README");
URLConnection conn = url.openConnection(proxy);

使用Proxy建立TCP链接:

SocketAddress addr = new InetSocketAddress("socks.example.com", 1080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
Socket socket = new Socket(proxy);
InetSocketAddress dest = new InetSocketAddress("server.example.org", 1234);

socket.connect(dest);

同样可以有另外一种方式让TCP链接使用代理:

Socket socket = new Socket(Proxy.NO_PROXY);

socket.connect(new InetAddress("localhost", 1234));

注意:参数只能是SOCKS或者DIRECT

4)   ProxySelector

使用JAVA SE5.0,开发者可以很灵活的使用proxies。

但是仍然有需求能够动态决定使用哪个代理,比如要做代理服务器的负载均衡,或者要指定一些代理服务器,用目前的api则会显得相当笨重。

ProxySelector闪亮登场。

一言概之,ProxySelector就是告诉程序去用哪个代理。如果不这样做,请看下面的问题:

URL url = newURL("http://java.example.org/index.html");

URLConnection conn = url.openConnection();

InputStream in = conn.getInputStream();

走到这里,HTTP协议handler被激活,并且要问proxySelector,类似如下谈话:

Handler:嘿,老弟,我要连接java.example.org,要通过代理吗?

PS:你要用什么协议?

Handler:HTTP啦

PS:默认端口?

Handler:我瞅瞅。。。嗯,就是默认端口

PS:明白,你用webcache.example.com:8080作为代理去连

Handler:多谢。数秒后。。。用不了啊,还有别的选择吗

PS:用webcache.example.com2:8080试试?

Handler:可以工作了,3q

从上面的对话我们可以明白ProxySelector是驱动型。如果你要的不在默认选项里,你可以自己定义替代品。

我们看下ProxySelector是如何定义的:

public abstract class ProxySelector {
        public static ProxySelector getDefault();
        public static void setDefault(ProxySelector ps);
        public abstract List<Proxy> select(URI uri);
        public abstract void connectFailed(URI uri,
                SocketAddress sa, IOException ioe);
}

ProxySelector是一个抽象类:2个抽象方法set和get本身,2个抽象方法让协议handlers可以决定使用哪个代理或者哪个代理使用不了。

如果要提供自己重写的ProxySelector,你只需要继承这个类,提供一个接口给这两个抽象方法,然后调用ProxySelector.setDefault()把你创建的实例传进去。

在讨论如何写ProxySelector之前,我们看下默认的情况。

J2SE5.0提供了一个默认的实现类可以向后兼容。这个默认的ProxySelector将会检测systemproperties,并决定使用哪个代理。

注意,在window系统和Gnome2.x平台上,可以告诉默认的ProxySelector使用systemproxysettings。如果system
property java.net.useSystemProxies设定为true(默认是false),则默认的ProxySelector将会使用这些设定。

下面我们测试下如何写,并且安装一个新的ProxySelector。

一般我们会为这些协议提供不止一个代理地址,如果一个失败了,我们可以试另一个,有些失败的代理地址,我们可以从代理列表中移除掉。

我们只要写一个ProxySelector的子类,并且实现select()和connectFailed()方法。

Select()方法在准备链接目标地址前被protocolhandlers调用。返回值是一组Proxy的list。

URL url = new URL("http://java.example.org/index.html");

InputStream in = url.openStream();

List<Proxy> l = ProxySelector.getDefault().select(newURI("http://java.example.org/"));

在我们实现中,我们需要做的就是判断这个协议是否是http或者https,如果是的话将会返回一个proxy的list,否则的话(Socks,FTP)将会返回默认的。

public class MyProxySelector extends ProxySelector {

// Keep a reference on theprevious default

ProxySelector defsel = null;

/*

* Inner class representinga Proxy and a few extra data

*/

class InnerProxy {

Proxy proxy;

SocketAddress addr;

// How many timesdid we fail to reach this proxy?

int failedCount = 0;

InnerProxy(InetSocketAddress a) {

addr = a;

proxy = newProxy(Proxy.Type.HTTP, a);

}

SocketAddressaddress() {

return addr;

}

Proxy toProxy() {

returnproxy;

}

int failed() {

return++failedCount;

}

}

/*

* A list of proxies,indexed by their address.

*/

HashMap<SocketAddress,InnerProxy> proxies = new HashMap<SocketAddress, InnerProxy>();

MyProxySelector(ProxySelectordef) {

// Save the previousdefault

defsel = def;

// Populate the HashMap(List of proxies)

InnerProxy i = newInnerProxy(new InetSocketAddress("webcache1.example.com", 8080));

proxies.put(i.address(),i);

i = new InnerProxy(newInetSocketAddress("webcache2.example.com", 8080));

proxies.put(i.address(),i);

i = new InnerProxy(newInetSocketAddress("webcache3.example.com", 8080));

proxies.put(i.address(),i);

}

/*

* This is the method thatthe handlers will call.

* Returns a List ofproxy.

*/

publicjava.util.List<Proxy> select(URI uri) {

// Let‘s stick tothe specs.

if (uri == null) {

throw newIllegalArgumentException("URI can‘t be null.");

}

/*

* If it‘s a http(or https) URL, then we use our own

* list.

*/

String protocol =uri.getScheme();

if("http".equalsIgnoreCase(protocol) ||

"https".equalsIgnoreCase(protocol)) {

ArrayList<Proxy>l = new ArrayList<Proxy>();

for(InnerProxy p : proxies.values()) {

l.add(p.toProxy());

}

return l;

}

/*

* Not HTTP or HTTPS(could be SOCKS or FTP)

* defer to thedefault selector.

*/

if (defsel != null){

returndefsel.select(uri);

} else {

ArrayList<Proxy> l = new ArrayList<Proxy>();

l.add(Proxy.NO_PROXY);

return l;

}

}

/*

* Method called by thehandlers when it failed to connect

* to one of the proxiesreturned by select().

*/

public voidconnectFailed(URI uri, SocketAddress sa, IOException ioe) {

// Let‘s stick tothe specs again.

if (uri == null ||sa == null || ioe == null) {

throw newIllegalArgumentException("Arguments can‘t be null.");

}

/*

* Let‘s lookup forthe proxy

*/

InnerProxy p = proxies.get(sa);

if (p !=null) {

/*

*It‘s one of ours, if it failed more than 3 times

*let‘s remove it from the list.

*/

if(p.failed() >= 3)

proxies.remove(sa);

} else {

/*

*Not one of ours, let‘s delegate to the default.

*/

if(defsel != null)

defsel.connectFailed(uri, sa, ioe);

}

}

}

时间: 2024-10-10 22:33:54

Java Networking and Proxies(译文)的相关文章

Java Networking

1 Java Networking 2 Java Networking: Socket 3 Java Networking: ServerSocket 4 Java Networking: UDP DatagramSocket 5 Java Networking: URL + URLConnection 6 Java Networking: JarURLConnection 7 Java Networking: InetAddress 8 Java Networking: Protocol De

Java Networking: Socket

Java Networking 1 Java Networking 2 Java Networking: Socket 3 Java Networking: ServerSocket 4 Java Networking: UDP DatagramSocket 5 Java Networking: URL + URLConnection 6 Java Networking: JarURLConnection 7 Java Networking: InetAddress 8 Java Network

Java Networking: UDP DatagramSocket (翻译)

原文:http://tutorials.jenkov.com/java-networking/udp-datagram-sockets.html UDP vs. TCP Sending Data via a DatagramSocket Receiving Data via a DatagramSocket DatagramSocket's are Java's mechanism for network communication via UDP instead of TCP. UDP is

Java Networking: InetAddress

The InetAddress is Java's representation of an IP address. Instances of this class are used together with UDP DatagramSockets and normal Socket's and ServerSocket's. Creating an InetAddress Instance InetAddress has no public contructor, so you must o

Java Networking Related (Java Examples in a Nutshell 3rd Edition)

Examples to: Use URL class to parse URLs and download the network resources specified by a URL Use URLConnection class to gain more cntrl over the downloading of network resources Write client programs that use the Socket class to communicate over th

Java NIO1

发现了一个很好的学习Java的外国网站,英语都是很简单的啦,看英语舒服些,关于NIO的系列就直接参照此网站了,而且是英语的! http://tutorials.jenkov.com/ Java NIO (New IO,也有人叫非阻塞IO) is an alternative IO API for Java (from Java 1.4), meaning alternative to the standard Java IO and Java Networking API's. Java NIO

Java DNS查询内部实现

源码分析 在Java中,DNS相关的操作都是通过通过InetAddress提供的API实现的.比如查询域名对应的IP地址: String dottedQuadIpAddress = InetAddress.getByName( "blog.arganzheng.me" ).getHostAddress(); 或者反过来IP对应域名: InetAddress[] addresses = InetAddress.getAllByName("8.8.8.8"); // i

atitit. 集合groupby 的实现(2)---自定义linq查询--java .net php

atitit.  集合groupby 的实现(2)---自定义linq查询--java .net php 实现方式有如下 1. Linq的实现原理流程(ati总结) 1 2. groupby  与 事先排序 2 3. #----------聚合函数 2 4. 自定义linq查询Linq .from(li) .groupBy("url","user" ) .select("url", count().as("countx"), 

Java数据报套接字

这篇博文是本文学习<Java网络程序设计>书中第5章数据报套接字的学习总结.初学者网友学习这篇Java数据报套接字文章,如果难于理解文章前面理论部分,可以先运行后面的程序,边看运行后面的程序边理解前面的原理,这对初学者是最好的方法.所有源代码都在文章后面我的github链接代码中. --惠州学院13网络工程 吴成兵 20160609 目录 1 目录 1 一 数据报套接字概述 二 DatagramPacket 21 创建DatagramPacket对象 211 创建的DatagramPacket