10

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

背景稍微复杂一些。

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

为了解决这个问题,我开始向后工作:我可以避免首先调用本机库吗?这导致我出于各种原因查看sun.net.spi.DefaultProxySelector,它负责为给定的 URI 找到一个合适的代理(或不)。那里有一个地方可以进行本地方法调用,这就是 JVM 崩溃发生的地方。如果我能避免那个电话,我会做生意的。

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

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

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

事实证明,协议是socket(我曾假设它可能类似于ldap,但不是)。这个事实和我被证明的假设向我表明,LDAP-realm-land 中的某个地方正在手动打开一个直接套接字(即不使用类似 anHttpUrlConnection或其他抽象的东西)。果然,Sun 创作的 LDAP-JNDI 桥接器就是这样做的。传入的 URI 是socket://somehost:389.

因此,尽管事实上我没有设置任何代理信息,没有配置任何东西或做任何事情,除了使用直接默认值之外,从这一切来看,事实证明JDK 尝试使用 SOCKS 代理。 有关详细信息,请参阅setImpl()java.net.Socket364 行左右的SocksSocketImpl.java方法并对其进行跟踪。

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

结果——再一次相信,尽管我或 GlassFish 没有更改配置,但DefaultProxySelector由于某种原因将其hasSystemProxies字段设置为,true由花园品种 Sun LDAP 连接创建的花园品种套接字导致本机查找用于 SOCKS 代理服务器。也许这只是我,但这让我觉得很疯狂。

那么有没有人读过这篇文章——也许你在 JDK 团队,或者认识某个人,或者知道这里的历史——知道为什么java.net.Socket总是寻找 SOCKS 代理的默认实现?

更新:我可以看到答案是:因此,如果您在某处设置了系统代理,并且它启用了 SOCKS,则所有内容都会通过它。但是,如果默认值java.net.useSystemProxiesfalse,那么对于 SOCKS 代理进行搜索(默认情况下)有什么意义呢?

4

1 回答 1

4

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

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

我认为您(或领导当前对该 Glassfish 错误进行调查的任何人)可能会以错误的方式处理此问题:与其尝试颠覆 JRE 选择套接字 impl 和代理的方式,不如修复默认值ProxySelector和/或操作系统本机调用,因此当 Java 对有关代理的信息进行标准查询时,事情不会中断。我认为修复是在代理查找过程和类的内部,而不是更高层。


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

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

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

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

于 2013-11-01T21:31:26.640 回答