Java ProxySelector 未定义行为
Java ProxySelector undefined behaviour
我正在 java 网络中试验代理。我有 read 有关它们的文档,目前正在测试 ProxySelector。
我注意到此 ProxySelector 在与 HttpURLConnection 一起使用时和与 Socket 一起使用时有两种行为 class
使用此代码与 HttpUrlConnection 一起使用时
class CustomSelector extends ProxySelector
{
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
{
System.out.println("Failed:"+uri);
System.out.println("Address:"+sa);
System.out.println("Exception:"+sa);
System.out.println("=========================");
}
}
public static void main(String[] args)throws Exception
{
ProxySelector.setDefault(new CustomSelector());
HttpURLConnection con=(HttpURLConnection)new URL("http://192.168.1.2:2000/Test")
.openConnection();
System.out.println(con.getResponseMessage());
con.disconnect();
}
我得到了预期的输出
Selecting
===============
Failed:http://192.168.1.2:2000/Test
Address:localhost/127.0.0.1:5000
Exception:localhost/127.0.0.1:5000
=========================
Failed:http://192.168.1.2:2000/Test
Address:localhost/127.0.0.1:8000
Exception:localhost/127.0.0.1:8000
=========================
Not-Implemented
这是有道理的,因为端口 5000 和 8000 只是虚拟端口,上面没有服务器 运行,因此连接失败,最终转到 NO_PROXY,它直接连接到我的自定义 HttpServer 运行 在端口 2000 上 returns 没有为所有的东西实现
现在我使用相同的过程使用套接字。我再次验证我的服务器是 运行 端口 2000
class CustomSelector extends ProxySelector
{
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
{
System.out.println("Failed:"+uri);
System.out.println("Address:"+sa);
System.out.println("Exception:"+sa);
System.out.println("=========================");
}
}
public static void main(String[] args)throws Exception
{
ProxySelector.setDefault(new CustomSelector());
try(Socket client=new Socket())
{
System.out.println("Connecting");
client.connect(new InetSocketAddress(InetAddress.getLocalHost(),2000));
System.out.println("Connected");
}
}
我得到这个输出
Connecting
Selecting
===============
Failed:socket://DESKTOP-1N0I046:2000
Address:localhost/127.0.0.1:5000
Exception:localhost/127.0.0.1:5000
=========================
Failed:socket://DESKTOP-1N0I046:2000
Address:localhost/127.0.0.1:8000
Exception:localhost/127.0.0.1:8000
=========================
Exception in thread "main" java.net.SocketException: Socket closed
at java.base/sun.nio.ch.NioSocketImpl.beginConnect(NioSocketImpl.java:498)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:580)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at java.base/java.net.Socket.connect(Socket.java:583)
at n_networking.proxy.TCPClient.main(TCPClient.java:237)
这不应该发生,因为代理列表中的最后一个选项是 NO_PROXY,这意味着没有任何应该成功的代理的直接连接,但似乎 ProxySelector 从未使用列表中的最后一个选项
更奇怪的是,如果我将 ProxyType 从 SOCKS 更改为 HTTP,如下所示。我知道这在这种情况下没有意义,但这只是为了测试目的
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
然后一切正常
输出:
Connecting
Selecting
===============
Connected
出于某种原因,它会跳过所有 HTTP 代理类型,甚至不会进行测试。
我已经将 Sockets 与 Proxy.HTTP 一起使用,它工作得很好,因为它在发送数据之前先发出 CONNECT 命令。
这是我用于这两个测试用例的虚拟服务器
public static void main(String[] args)throws Exception
{
try(ServerSocket server=new ServerSocket(2000,0,InetAddress.getLocalHost()))
{
System.out.println("Main Server Started");
try(Socket socket=server.accept())
{
System.out.println("Accepted");
socket.getOutputStream().write("HTTP/1.1 501 Not-Implemented\r\n\r\n".getBytes());
socket.getOutputStream().flush();
}
}
}
为什么会有这些差异?我正在使用 jdk 17.0.2 和 windows 10
这似乎是 JDK 中的错误:JDK-7141231
尽管理论上 java.net.SocksSocketImpl
supporting proxy failover;实际上,这显然行不通,因为在第一次失败的连接尝试后,套接字已关闭,但相同的已关闭套接字用于任何后续连接尝试,因此失败并显示“套接字关闭”(您正在看到)。
之所以将代理类型更改为 HTTP“有效”是因为它执行直接连接,ignoring all other specified proxies。
我正在 java 网络中试验代理。我有 read 有关它们的文档,目前正在测试 ProxySelector。
我注意到此 ProxySelector 在与 HttpURLConnection 一起使用时和与 Socket 一起使用时有两种行为 class
使用此代码与 HttpUrlConnection 一起使用时
class CustomSelector extends ProxySelector
{
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
{
System.out.println("Failed:"+uri);
System.out.println("Address:"+sa);
System.out.println("Exception:"+sa);
System.out.println("=========================");
}
}
public static void main(String[] args)throws Exception
{
ProxySelector.setDefault(new CustomSelector());
HttpURLConnection con=(HttpURLConnection)new URL("http://192.168.1.2:2000/Test")
.openConnection();
System.out.println(con.getResponseMessage());
con.disconnect();
}
我得到了预期的输出
Selecting
===============
Failed:http://192.168.1.2:2000/Test
Address:localhost/127.0.0.1:5000
Exception:localhost/127.0.0.1:5000
=========================
Failed:http://192.168.1.2:2000/Test
Address:localhost/127.0.0.1:8000
Exception:localhost/127.0.0.1:8000
=========================
Not-Implemented
这是有道理的,因为端口 5000 和 8000 只是虚拟端口,上面没有服务器 运行,因此连接失败,最终转到 NO_PROXY,它直接连接到我的自定义 HttpServer 运行 在端口 2000 上 returns 没有为所有的东西实现
现在我使用相同的过程使用套接字。我再次验证我的服务器是 运行 端口 2000
class CustomSelector extends ProxySelector
{
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.SOCKS,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
{
System.out.println("Failed:"+uri);
System.out.println("Address:"+sa);
System.out.println("Exception:"+sa);
System.out.println("=========================");
}
}
public static void main(String[] args)throws Exception
{
ProxySelector.setDefault(new CustomSelector());
try(Socket client=new Socket())
{
System.out.println("Connecting");
client.connect(new InetSocketAddress(InetAddress.getLocalHost(),2000));
System.out.println("Connected");
}
}
我得到这个输出
Connecting
Selecting
===============
Failed:socket://DESKTOP-1N0I046:2000
Address:localhost/127.0.0.1:5000
Exception:localhost/127.0.0.1:5000
=========================
Failed:socket://DESKTOP-1N0I046:2000
Address:localhost/127.0.0.1:8000
Exception:localhost/127.0.0.1:8000
=========================
Exception in thread "main" java.net.SocketException: Socket closed
at java.base/sun.nio.ch.NioSocketImpl.beginConnect(NioSocketImpl.java:498)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:580)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at java.base/java.net.Socket.connect(Socket.java:583)
at n_networking.proxy.TCPClient.main(TCPClient.java:237)
这不应该发生,因为代理列表中的最后一个选项是 NO_PROXY,这意味着没有任何应该成功的代理的直接连接,但似乎 ProxySelector 从未使用列表中的最后一个选项
更奇怪的是,如果我将 ProxyType 从 SOCKS 更改为 HTTP,如下所示。我知道这在这种情况下没有意义,但这只是为了测试目的
@Override
public List<Proxy> select(URI uri)
{
System.out.println("Selecting");
System.out.println("===============");
return List.of
(
new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",5000))
,new Proxy(Proxy.Type.HTTP,new InetSocketAddress("localhost",8000))
,Proxy.NO_PROXY
);
}
然后一切正常
输出:
Connecting
Selecting
===============
Connected
出于某种原因,它会跳过所有 HTTP 代理类型,甚至不会进行测试。
我已经将 Sockets 与 Proxy.HTTP 一起使用,它工作得很好,因为它在发送数据之前先发出 CONNECT 命令。
这是我用于这两个测试用例的虚拟服务器
public static void main(String[] args)throws Exception
{
try(ServerSocket server=new ServerSocket(2000,0,InetAddress.getLocalHost()))
{
System.out.println("Main Server Started");
try(Socket socket=server.accept())
{
System.out.println("Accepted");
socket.getOutputStream().write("HTTP/1.1 501 Not-Implemented\r\n\r\n".getBytes());
socket.getOutputStream().flush();
}
}
}
为什么会有这些差异?我正在使用 jdk 17.0.2 和 windows 10
这似乎是 JDK 中的错误:JDK-7141231
尽管理论上 java.net.SocksSocketImpl
supporting proxy failover;实际上,这显然行不通,因为在第一次失败的连接尝试后,套接字已关闭,但相同的已关闭套接字用于任何后续连接尝试,因此失败并显示“套接字关闭”(您正在看到)。
之所以将代理类型更改为 HTTP“有效”是因为它执行直接连接,ignoring all other specified proxies。