您的位置:首页 > 编程语言 > Java开发

Java Networking and Proxies(译文)

2014-12-02 16:59 162 查看


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

原文地址:

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



概述

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

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

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

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



系统特性

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

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

有两个方法设置systemproperties

借助虚拟机使用命令行

使用
System.setProperty(String,String)
方法


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



2.1HTTP

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

http.proxyHost


http.proxyPort
:默认值80


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


我们来看一些例子:


例一:

$java-Dhttp.proxyHost=webcache.example.comGetURL

所有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链接。

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

//Setthehttpproxytowebcache.example.com:8080




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

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



//Nextconnectionwillbethroughproxy.

URLurl=newURL("http://java.example.org/");

InputStreamin=url.openStream();



//Now,let's'unset'theproxy.

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



//Fromnowonhttpconnectionswillbedonedirectly.

即便是有些繁琐,代码工作正常,但是在多线程的应用中会变得有些微妙。记住,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.comGetURL


HTTPURL-
》HTTPProxy


FTP-
》SOCKSProxy


3)Proxy类

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



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

DIRECT:不使用代理

HTTP:使用HTTP协议的代理

SOCKS:使用SOCKSv4或者v5协议的代理

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

SocketAddressaddr=new

InetSocketAddress("webcache.example.com",8080);

Proxyproxy=newProxy(Proxy.Type.HTTP,addr);


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

怎么使用呢?

URLurl=newURL("http://java.example.org/");

URConnectionconn=url.openConnection(proxy);


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

URLurl2=newURL("http://infos.example.com/");


URLConnectionconn2=url2.openConnection(Proxy.NO_PROXY);

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

SocketAddressaddr=newInetSocketAddress("socks.example.com",1080);

Proxyproxy=newProxy(Proxy.Type.SOCKS,addr);

URLurl=newURL("ftp://ftp.gnu.org/README");

URLConnectionconn=url.openConnection(proxy);




使用Proxy建立TCP链接:

SocketAddressaddr=newInetSocketAddress("socks.example.com",1080);

Proxyproxy=newProxy(Proxy.Type.SOCKS,addr);

Socketsocket=newSocket(proxy);

InetSocketAddressdest=newInetSocketAddress("server.example.org",1234);


socket.connect(dest);

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

Socketsocket=newSocket(Proxy.NO_PROXY);

socket.connect(newInetAddress("localhost",1234));


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



4)ProxySelector

使用J***ASE5.0,开发者可以很灵活的使用proxies。


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


ProxySelector闪亮登场。


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

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

URLConnectionconn=url.openConnection();

InputStreamin=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是如何定义的:

publicabstractclassProxySelector{

publicstaticProxySelectorgetDefault();

publicstaticvoidsetDefault(ProxySelectorps);

publicabstractList<Proxy>select(URIuri);

publicabstractvoidconnectFailed(URIuri,

SocketAddresssa,IOExceptionioe);

}


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。

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

InputStreamin=url.openStream();

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

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

publicclassMyProxySelectorextendsProxySelector{


//Keepareferenceonthepreviousdefault


ProxySelectordefsel=null;




/*


*InnerclassrepresentingaProxyandafewextradata


*/


classInnerProxy{


Proxyproxy;


SocketAddressaddr;


//Howmanytimesdidwefailtoreachthisproxy?


intfailedCount=0;



InnerProxy(InetSocketAddressa){


addr=a;


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


}




SocketAddressaddress(){


returnaddr;


}




ProxytoProxy(){


returnproxy;


}




intfailed(){


return++failedCount;


}


}




/*


*Alistofproxies,indexedbytheiraddress.


*/


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




MyProxySelector(ProxySelectordef){


//Savethepreviousdefault


defsel=def;




//PopulatetheHashMap(Listofproxies)


InnerProxyi=newInnerProxy(newInetSocketAddress("webcache1.example.com",8080));


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


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


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


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


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


}




/*


*Thisisthemethodthatthehandlerswillcall.


*ReturnsaListofproxy.


*/


publicjava.util.List<Proxy>select(URIuri){


//Let'ssticktothespecs.


if(uri==null){


thrownewIllegalArgumentException("URIcan'tbenull.");


}




/*


*Ifit'sahttp(orhttps)URL,thenweuseourown


*list.


*/


Stringprotocol=uri.getScheme();


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

"https".equalsIgnoreCase(protocol)){


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


for(InnerProxyp:proxies.values()){

l.add(p.toProxy());


}


returnl;


}




/*


*NotHTTPorHTTPS(couldbeSOCKSorFTP)


*defertothedefaultselector.


*/


if(defsel!=null){


returndefsel.select(uri);


}else{

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

l.add(Proxy.NO_PROXY);


returnl;


}


}




/*


*Methodcalledbythehandlerswhenitfailedtoconnect


*tooneoftheproxiesreturnedbyselect().


*/


publicvoidconnectFailed(URIuri,SocketAddresssa,IOExceptionioe){


//Let'ssticktothespecsagain.


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


thrownewIllegalArgumentException("Argumentscan'tbenull.");


}




/*


*Let'slookupfortheproxy


*/


InnerProxyp=proxies.get(sa);


if(p!=null){


/*


*It'soneofours,ifitfailedmorethan3times


*let'sremoveitfromthelist.


*/


if(p.failed()>=3)

proxies.remove(sa);


}else{


/*


*Notoneofours,let'sdelegatetothedefault.


*/


if(defsel!=null)

defsel.connectFailed(uri,sa,ioe);


}


}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: