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
借助虚拟机使用命令行
使用
所有的代理被定义为一个主机名+一个端口号。
2.1HTTP
有三个properties可以指定HTTP协议的proxy
例一:
$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
在第二个例子中,代理服务器还是“
之前提到的,这些设定会影响所有的HTTP链接。
当然,我们可以使用代码让这个设定变得轻巧,动态:
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
ftp.nonProxyHosts
这个API就是Proxy类,定义了代理的3种类型:
DIRECT:不使用代理
HTTP:使用HTTP协议的代理
SOCKS:使用SOCKSv4或者v5协议的代理
所以,建立一个HTTP代理将用以下方法:
记住!这个Proxy实例只是代表着一个代理的定义。
怎么使用呢?
同样的机制可以指定一个特殊的链接不使用代理,用这种方式不用定义Proxy的实例。
URLConnectionconn2=url2.openConnection(Proxy.NO_PROXY);
一样的,可以用SOCKS代理进行连接,方式也一样
使用Proxy建立TCP链接:
socket.connect(dest);
同样可以有另外一种方式让TCP链接使用代理:
注意:参数只能是SOCKS或者DIRECT
但是仍然有需求能够动态决定使用哪个代理,比如要做代理服务器的负载均衡,或者要指定一些代理服务器,用目前的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是如何定义的:
ProxySelector是一个抽象类:2个抽象方法set和get本身,2个抽象方法让协议handlers可以决定使用哪个代理或者哪个代理使用不了。
如果要提供自己重写的ProxySelector,你只需要继承这个类,提供一个接口给这两个抽象方法,然后调用ProxySelector.setDefault()把你创建的实例传进去。
在讨论如何写ProxySelector之前,我们看下默认的情况。
J2SE5.0提供了一个默认的实现类可以向后兼容。这个默认的ProxySelector将会检测systemproperties,并决定使用哪个代理。
注意,在window系统和Gnome2.x平台上,可以告诉默认的ProxySelector使用systemproxysettings。如果system
property
下面我们测试下如何写,并且安装一个新的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);
}
}
}
JavaNetworkingandProxies
比较早的文章,正好在研究javaproxy的用法,就翻译了一下
原文地址:
概述
在如今的网络环境下,尤其是合作项目,项目开发者不得不频繁的处理代理问题。
有时候项目使用系统默认参数即可,但有些情况下,希望能够详密掌控代理的使用情况。
很多项目会提供用户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
:
:默认80
:
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);
}
}
}
相关文章推荐
- Java networking and proxies
- Java Networking and Proxies setting by options and checking etc
- Java Networking and Proxies
- Java and XML, 3rd Edition 译文:Preface
- Java and XML, 3rd Edition 译文:Chapter 1. Introduction
- 9 Differences between TCP and UDP Protocol-Java Network Interview Question-译文
- Java and XML, 3rd Edition 译文:OverView
- Java Proxies and UndeclaredThrowableException
- Java Testing and Design - 3. Acknowledgments
- Plotting Engineering and Scientific Data using Java
- Borland Eyeing the Chasm Between Java and .NET
- Java and GIS, Part 1: Intro to GIS (From www.java.net)
- JVM - Concept and Lifetime (From book "Inside the Java Virtual Machine")
- C# vs. Java:相反的思维方式 (译文)
- Swing and SWT: A Tale of Two Java GUI Libraries
- Java and GIS, Part 2: Mobile LBS (From www.java.net)
- Java基教--异常与错误区别 Error and Exception
- Synchronization and the Java Memory Model
- Java Testing and Design - 1. Foreword
- (Page 2 of 3 )A walking tour of JavaBeans 2 :What JavaBeans is, how it works, and why you want to use it