问题 如何在java.net.URLConnection上指定本地地址?


我的Tomcat实例正在侦听多个IP地址,但我想控制打开时使用的源IP地址 URLConnection

我怎么指定这个?


5491
2017-09-18 11:12


起源

如果我的tomcat正在侦听具有不同网关(在不同子网上)的两个IP地址,如果我指定为代理的IP地址的网关暂时不可用,将会发生什么情况,openConnection会抛出异常还是会回退到另一个网关/ IP? - stian


答案:


这应该是诀窍:

URL url = new URL(yourUrlHere);
Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
    new InetSocketAddress( 
        InetAddress.getByAddress(
            new byte[]{your, ip, interface, here}), yourTcpPortHere));
URLConnection conn = url.openConnection(proxy);

你完成了。 不要忘记很好地处理异常,并且当然会更改值以适合您的场景。

啊,我省略了import语句


6
2017-09-18 12:34



这失败了 “java.lang.IllegalArgumentException:type DIRECT与地址my.ip.address.here不兼容”。潜入Proxy的源代码,我看到会抛出这个异常 每当 尝试输入DIRECT。 - user359996
这对我不起作用,我无法理解为什么。没有代理,没关系。使用代理和虚拟IP(我的localhost上的第二个IP地址)指定我得到一个 ConnectException。用套接字测试而不是 URLConnection 使用4-arg套接字构造函数也可以正常工作,其中指定了本地地址。还有其他人可以确认他们是否已经让上述解决方案在本地接口上使用第二个IP? - KomodoDave
我应该使用哪个本地端口?是不是应该由较低级别的TCP堆栈设置以避免冲突并使端口可重用? - vbence
此代码无法正常工作。我已经评论过了 java.net.Proxy 因为JDK 1.5并且总是会在构造函数中抛出异常。见第一条评论。这个答案是不正确的 - pedrofb


使用Apache commons HttpClient我也发现了以下工作(为了清楚起见,删除了try / catch):

HostConfiguration hostConfiguration = new HostConfiguration();
byte b[] = new byte[4];
b[0] = new Integer(192).byteValue();
b[1] = new Integer(168).byteValue();
b[2] = new Integer(1).byteValue();
b[3] = new Integer(11).byteValue();

hostConfiguration.setLocalAddress(InetAddress.getByAddress(b));

HttpClient client = new HttpClient();
client.setHostConfiguration(hostConfiguration);
GetMethod method = new GetMethod("http://remoteserver/");
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler(3, false));
int statusCode = client.executeMethod(method);

if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: " + method.getStatusLine());
}

byte[] responseBody = method.getResponseBody();
System.out.println(new String(responseBody));");

但是,我仍然想知道如果IP的网关关闭会发生什么(在这种情况下为192.168.1.11)。下一个网关会被尝试还是会失败?


3
2017-09-18 14:45



使用4.1代码库(可能更早?),您可以在一行中指定本地IP地址: object.getParams()。setParameter(“http.route.local-address”,ipAddress),哪里 目的要么是 HttpClient的 或者 HttpRequest的。 - user359996


明显的可移植方式是在URL.openConnection中设置代理。代理可以在本地主机中,然后您可以编写一个非常简单的代理来绑定客户端套接字的本地地址。

如果无法修改URL所连接的源,则可以在调用URL构造函数时替换URLStreamHandler,也可以通过URL.setURLStreamHandlerFactory全局替换URLStreamHandler。然后,URLStreamHandler可以委托默认的http / https处理程序,修改openConnection调用。

更极端的方法是完全替换处理程序(可能在JRE中扩展实现)。或者,可以使用备用(开源)http客户端。


1
2017-09-18 12:19





手动设置插座工作正常...

private HttpsURLConnection openConnection(URL src, URL dest, SSLContext sslContext)
        throws IOException, ProtocolException {
    HttpsURLConnection connection = (HttpsURLConnection) dest.openConnection();
    HttpsHostNameVerifier httpsHostNameVerifier = new HttpsHostNameVerifier();
    connection.setHostnameVerifier(httpsHostNameVerifier);
    connection.setConnectTimeout(CONNECT_TIMEOUT);
    connection.setReadTimeout(READ_TIMEOUT);
    connection.setRequestMethod(POST_METHOD);
    connection.setRequestProperty(CONTENT_TYPE, SoapConstants.CONTENT_TYPE_HEADER);
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setSSLSocketFactory(sslContext.getSocketFactory());
    if ( src!=null ) {
        InetAddress inetAddress = InetAddress.getByName(src.getHost());
        int destPort = dest.getPort();
        if ( destPort <=0 ) 
            destPort=SERVER_HTTPS_PORT;
        int srcPort = src.getPort();
        if ( srcPort <=0 ) 
            srcPort=CLIENT_HTTPS_PORT;
         connectionSocket = connection.getSSLSocketFactory().createSocket(dest.getHost(), destPort, inetAddress, srcPort);
    }
    connection.connect();
    return connection;
}    

1
2017-07-08 15:09



只是为了确保我正确地理解这一点(因为我已经测试了它并且它有效),只有你创建一个套接字的部分我想对其进行一些澄清。 createSocket()方法强制底层套接字工厂使用您刚刚创建的套接字?因为我没有看到在其他地方使用的connectionSocket变量。该文档仅提到“返回在给定端口上连接到指定主机的现有套接字上分层的套接字”。 - schwarz