简体   繁体   English

Java ProxySelector 未定义行为

[英]Java ProxySelector undefined behaviour

I am experimenting with Proxies in java.networking.我正在 java.networking 中试验代理。 I have read the documentation regarding them and am currently testing ProxySelector.我已经阅读了有关它们的文档并且目前正在测试 ProxySelector。

I notice 2 types of behaviour of this ProxySelector when using with HttpURLConnection & when using with Socket class当与 HttpURLConnection 一起使用时以及与 Socket class 一起使用时,我注意到此 ProxySelector 的两种行为

When using with HttpUrlConnection with this code使用此代码与 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();
 }  

I get the expected output我得到了预期的 output

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

This makes sense because ports 5000 & 8000 are just dummy ports with no server running on it hence connection fails on them and it finally goes to NO_PROXY which directly connects to my custom HttpServer running on port 2000 which returns not implemented for everything这是有道理的,因为端口 5000 和 8000 只是虚拟端口,没有服务器在上面运行,因此连接失败,它最终转到 NO_PROXY,它直接连接到我在端口 2000 上运行的自定义 HttpServer,返回未实现的所有内容

Now i work with Sockets using the same procedure.现在我使用相同的程序与 Sockets 合作。 Again i have verified that my server is running on port 2000我再次验证我的服务器正在端口 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");
   } 
  }     

I get this output我得到这个 output

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)

This should not happen as the last option in the Proxy List is NO_PROXY which means direct connection without any proxy which should succeed but it seems that the ProxySelector never uses that last option in the list这不应该发生,因为代理列表中的最后一个选项是 NO_PROXY,这意味着没有任何应该成功的代理的直接连接,但似乎 ProxySelector 从未使用列表中的最后一个选项

Whats more bizarre is that if i change my ProxyType from SOCKS to HTTP as follows.更奇怪的是,如果我将 ProxyType 从 SOCKS 更改为 HTTP,如下所示。 I know it doesn't make sense in this context but this is just for testing purposes我知道这在这种情况下没有意义,但这只是为了测试目的

   @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 
     ); 
   } 

Then everything works然后一切正常

Output: Output:

Connecting
Selecting
===============
Connected

For some reason it skips all HTTP proxy types and not even test then.出于某种原因,它会跳过所有 HTTP 代理类型,甚至不进行测试。

I have used Sockets with Proxy.HTTP and it works perfectly as it issues an CONNECT command first before sending data.我已经将 Sockets 与 Proxy.HTTP 一起使用,它工作得很好,因为它在发送数据之前先发出 CONNECT 命令。

Here is my dummy server i use for both these test cases这是我用于这两个测试用例的虚拟服务器

 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();
   }
 }
}

Why these differences?为什么会有这些差异? I am using jdk 17.0.2 with windows 10我正在使用 jdk 17.0.2 和 windows 10

This seems to be a bug in the JDK: JDK-7141231这似乎是 JDK 中的错误: JDK-7141231

Despite java.net.SocksSocketImpl in theory supporting proxy failover ;尽管java.net.SocksSocketImpl理论上支持代理故障转移 in reality this does apparently not work because after the first failed connection attempt the socket is closed, but that same closed socket is used for any subsequent connection attemps, which therefore fail with "Socket closed" (which you are seeing).实际上,这显然行不通,因为在第一次失败的连接尝试后,套接字已关闭,但相同的已关闭套接字用于任何后续连接尝试,因此失败并显示“套接字关闭”(您正在看到)。

The reason why changing the proxy type to HTTP "works" is because it performs a direct connection, ignoring all other specified proxies .将代理类型更改为 HTTP “有效”的原因是因为它执行直接连接, 忽略所有其他指定的代理

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM