java - 为什么 java.net.SocksSocketImpl 是 Java 中默认的 java.net.Socket 实现?

标签 java

问题很简短。 为什么 SOCKS 感知套接字实现是实现抽象 java.net.Socket 类的默认选择?我天真地期待 java.net。 PlainSocketImpl.

背景有点复杂。

我正试图杀死 GLASSFISH-12213 (或者真的我正在尝试解决它)。错误本身的细节并不是很重要——有一个非线程安全的 native 库,并且来自 GlassFish 编写的 LDAP 领域的一些并发使用会导致 JVM 崩溃。

为了解决这个问题,我开始逆向工作:我可以避免首先调用 native 库吗?由于各种原因,这导致我查看 sun.net.spi.DefaultProxySelector ,负责finding an appropriate proxy (or not) for a given URI .有一个 spot in there它进行 native 方法调用的地方,也是 JVM 崩溃发生的地方。如果我能避免那个电话,我就会做生意。

我可以避免调用的一种方法是,如果我可以确保 sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies") 的值错误的。如果是这样的话,那么我之前提到的本地方法就不会被调用。 false 是默认值,我在这方面根本没有修改任何东西。

(所以实际上它情况;在我观察到该错误的那台机器上运行的 GlassFish 实例中得到了证明。因此,我不确定这段代码如何可能仍在加载 native 库;这是另一天的主题。)

所以,在这条路上,我进一步支持:DefaultProxySelector.select(uri)< 中的 URI scheme 传递了什么协议(protocol) 调用?也许我可以以某种方式影响愚蠢的事情,以某种方式仍然跳过该本地调用。

事实证明,协议(protocol)是 socket(我原以为它可能类似于 ldap,但不是)。这个事实和我未证实的假设向我表明,LDAP 领域中的某处正在手动打开直接套接字(即不使用类似 HttpUrlConnection 或其他抽象的东西)。果然,Sun 编写的 LDAP-JNDI 桥就是这样做的;传入的 URI 是 socket://somehost:389

所以从这一切来看,尽管我没有设置任何代理信息,没有配置任何东西,也没有做任何事情,除了使用直接默认值,结果是JDK 尝试使用SOCKS 代理。参见the setImpl() method in java.net.Socketline 364 or so of SocksSocketImpl.java并追踪它以获取详细信息。

(这最终表明我可以通过简单地向混合中添加 socksNonProxyHosts=* 系统属性来跳过整个代码路径。哎呀,这种行为不应该是默认行为吗?)

因此,再次相信 DefaultProxySelector 出于某种原因具有其 hasSystemProxies field set to true尽管我或 GlassFish 没有更改配置,由普通 Sun LDAP 连接创建的普通套接字导致对 SOCKS 代理服务器进行 native 查找。也许只有我一个人如此,但这让我觉得很疯狂。

阅读本文的任何人——也许你在 JDK 团队中,或者认识某个人,或者知道这里的历史——知道为什么 java.net.Socket 的默认实现总是寻找 SOCKS 代理?

更新:我可以看到答案是:如果您在某处设置了系统代理,并且它启用了 SOCKS,那么所有内容都会流经它。但是,如果 java.net.useSystemProxies 的默认值是 false,那么(默认情况下)寻找 SOCKS 代理的意义何在?

最佳答案

默认套接字实现是 SocksSocketImpl 因为 JRE 可能 已通过系统属性 -DsocksProxyHost 和 - 从外部配置为使用 SOCKS DsocksProxyPortProxySelector.setDefault() 或通过 JRE 安装的默认 ProxySelector

PlainSocketImpl 不会查询这些属性或类(因为它是一个普通套接字,不应该知道任何关于代理的信息)所以这些外部配置将是被忽略 并非总是调用SocksSocketImpl 来检查它们。我同意当没有人对 JRE 说任何关于 SOCKS 的事情时,你得到一个 SocksSocketImpl 似乎很奇怪,但我猜 java.net.Socket 的架构ProxySelector 不允许它在 Socket 实例化时预选正确的实现。

我认为您(或领导当前对 Glassfish 错误的调查的任何人)可能会以错误的方式解决这个问题:与其试图颠覆 JRE 选择套接字实现和代理的方式,不如修复默认设置ProxySelector 和/或 OS native 调用,这样当 Java 对有关代理的信息进行标准查询时,事情就不会中断。我认为修复是在代理查找过程和类的内部,而不是更高层。


也许另一种问我所问的方式是:如果 DefaultProxySelector 可以告诉我用于套接字连接的代理,为什么 Socket 不首先调用它来帮助它选择一个合理的实现?

我认为问题在于 java.net.Socket 支持多种编程模型。有明显的 new Socket(host, port) 构造函数,但还有一个默认构造函数 new Socket() 可以先构造,然后在任意时间构造将来 可以调用其connect(SocketAddress) 方法。

因为 ProxySelector 可以用来确定是否应该使用代理的部分标准是远程主机的名称和要连接的远程端口(即信息提供给 connect),java.net.Socket 构造函数还为时过早,无法知道是否需要代理。

无论出于何种原因(我们可以假设历史原因吗?/向后兼容的原因?:),java.net.Socket 的构造函数是唯一设置 impl 的地方,而且,就像我说的,在某些情况下,判断是否需要代理还为时过早。

关于java - 为什么 java.net.SocksSocketImpl 是 Java 中默认的 java.net.Socket 实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19735305/

相关文章:

java - Java 中的包装类和泛型说明

java - 在 Java 中使用链式键获取嵌套 JSON 值

java - 如何为 main 方法编写 Junit,将 "Hello, World!"字符串打印到控制台并使用 Spring 框架

java - 如何在android中访问在线mysql数据库?

java - 在 Java 中开发功能强大的浏览器托管 UI 的选项有哪些?

java - 添加一个方面来捕获异常并返回 null

java - Spring MVC 绑定(bind)

java - 根据按钮链接在 webview 中显示特定的 url

java - 如何检查 BST 中的节点是否不可访问

java - Apache Ant 有没有办法在构建后更新 jar 文件?