简体   繁体   English

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

[英]Why is java.net.SocksSocketImpl the default java.net.Socket implementation in Java?

The question is brief. 问题很简短。 Why is a SOCKS-aware socket implementation the default choice for the implementation of the abstract java.net.Socket class? 为什么支持SOCKS的套接字实现是抽象java.net.Socket类实现的默认选择? Naïvely I'd expect java.net.PlainSocketImpl . 天真地,我期待java.net.PlainSocketImpl

The background is a bit more complicated. 背景有点复杂。

I'm trying to kill off GLASSFISH-12213 (or really I'm trying to work around it). 我正试图杀死GLASSFISH-12213 (或者我真的想要解决它)。 The details of the bug itself aren't very important--there's a native library that is not thread safe, and some concurrent usages of it from a GlassFish-authored LDAP realm crash the JVM. 错误本身的细节不是很重要 - 有一个本地库不是线程安全的,并且来自GlassFish编写的LDAP领域的一些并发使用会使JVM崩溃。

To work around it, I started working backwards: can I avoid having the native library called in the first place? 为了解决这个问题,我开始倒退了:我可以避免首先调用本机库吗? This led me for various reasons to look at sun.net.spi.DefaultProxySelector , which is in charge of finding an appropriate proxy (or not) for a given URI . 这导致我出于各种原因查看sun.net.spi.DefaultProxySelector ,它负责为给定的URI查找(或不是)适当的代理 There's a spot in there where it makes a native method call, and that is where the JVM crash occurs. 在那里有一个位置 ,它进行本机方法调用,这就是JVM崩溃发生的地方。 If I could avoid that call, I'd be in business. 如果我可以避免这个电话,我会做生意。

One way I could avoid that call would be if I could ensure that the value of sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies") false. 我可以避免该调用的一种方法是,如果我能确保sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies")值为false。 If that were the case, then the native method I mentioned earlier wouldn't be called. 如果是这种情况,那么我之前提到的本机方法将不会被调用。 false is the default and I haven't modified anything in this regard at all. false是默认值,我根本没有修改过这方面的任何内容。

(So actually it is the case; demonstrably proven in the GlassFish instance running on that machine where I observed the bug. I'm not sure how this code, therefore, could possibly still be loading the native library; that's a subject for another day.) (所以实际上就是这种情况;在我在机器上运行的GlassFish实例中证明了这一点,我发现了这个错误。我不确定这段代码是如何可能仍然加载本机库的;这是另一天的主题。)

So, punting on that path, I backed up further: what protocol was being passed as the URI scheme in the DefaultProxySelector.select(uri) call? 因此,在这条路径上,我进一步备份:在DefaultProxySelector.select(uri)调用中作为URI scheme传递了什么协议? Maybe I could then somehow influence the silly thing to still skip that native call somehow. 也许我可以以某种方式影响愚蠢的事情仍然以某种方式跳过本地电话。

As it turns out, the protocol was socket (I had assumed it was probably something like ldap , but no). 事实证明,协议是socket (我以为它可能类似于ldap ,但没有)。 This fact and my disproven assumption suggested to me that somewhere something in LDAP-realm-land was opening up a direct socket by hand (ie not using something like an HttpUrlConnection or some other abstraction). 这个事实和我的错误假设向我提出,在LDAP-realm-land中的某个地方正在手工打开一个直接套接字(即不使用类似HttpUrlConnection或其他抽象的东西)。 Sure enough, the Sun-authored LDAP-JNDI bridge does just this; 果然,Sun编写的LDAP-JNDI桥就是这样做的; the URI that is passed in is socket://somehost:389 . 传入的URI是socket://somehost:389

So from all this despite the fact I haven't set up any proxy information, configured anything or done anything other than use straight defaults , it turns out that the JDK attempts to use a SOCKS proxy. 所以尽管事实上我没有设置任何代理信息,配置任何东西或做除了使用直接默认值以外的任何事情 ,但事实证明JDK尝试使用SOCKS代理。 See the setImpl() method in java.net.Socket and line 364 or so of SocksSocketImpl.java and trace it through for details. 请参阅java.net.SocketsetImpl()方法SocksSocketImpl.java 364行左右,并查看详细信息。

(This finally suggests that I might be able to skip this whole codepath by simply adding a socksNonProxyHosts=* system property to the mix. Jeez, shouldn't that behavior be the default?) (这最终表明我可以通过简单地将socksNonProxyHosts=*系统属性添加到混合中来跳过整个代码路径.Jeez,这个行为不应该是默认的吗?)

As a result--and again, taking it on faith that the DefaultProxySelector for some reason is having its hasSystemProxies field set to true despite no changes in configuration by me or GlassFish, a garden variety socket created by a garden variety Sun LDAP connection is causing a native lookup for a SOCKS proxy server. 结果 - 并且再次认为DefaultProxySelector由于某种原因将其hasSystemProxies字段设置为true尽管我或GlassFish没有对配置进行任何更改,因此由花园种类的Sun LDAP连接创建的花园种类套接字导致SOCKS代理服务器的本机查找。 Maybe it's just me, but that strikes me as madness. 也许这只是我,但这让我感到疯狂。

So does anyone reading this--perhaps you are on the JDK team, or know someone who is, or know the history here--know why the default implementation of java.net.Socket always looks for a SOCKS proxy? 阅读本文的人也是如此 - 也许你是JDK团队的成员,或者知道某人是谁,或者知道这里的历史 - 知道为什么java.net.Socket的默认实现总是寻找SOCKS代理?

Update: I can see that the answer would be: so that if you have a system proxy set somewhere, and it's SOCKS-enabled, all stuff flows through it. 更新:我可以看到答案是:所以,如果你在某个地方设置了一个系统代理,并且它启用了SOCKS,那么所有东西都会流过它。 But if the default value of java.net.useSystemProxies is false , as it is, then what's the point of hunting (by default) for a SOCKS proxy? 但是,如果java.net.useSystemProxies的默认值为false ,那么SOCKS代理的搜索点(默认情况下)是什么?

The default socket implementation is SocksSocketImpl because the JRE might have been externally configured to use SOCKS via the system properties -D socksProxyHost and -D socksProxyPort or ProxySelector.setDefault() or via the default ProxySelector installed by the JRE. 默认套接字实现是SocksSocketImpl因为JRE 可能已经外部配置为通过系统属性-D socksProxyHost和-D socksProxyPortProxySelector.setDefault()或通过JRE安装的默认ProxySelector来使用SOCKS。

PlainSocketImpl does not consult these properties or classes (because it is a plain socket and should not know anything about proxies) and so these external configurations would be ignored were not SocksSocketImpl always invoked to check on them. PlainSocketImpl 参考这些属性或类(因为它是一个普通的插座,不应该知道代理的任何东西),因此这些外部配置将被忽略SocksSocketImpl总是调用来检查它们。 I agree it seems odd that you get a SocksSocketImpl when nobody has said anything about SOCKS to the JRE, but I guess the architecture of java.net.Socket and ProxySelector does not allow it to preselect the correct impl at the time of Socket instantiation. 我同意,当没有人向JRE SocksSocketImpl有关SOCKS的任何内容时,你得到一个SocksSocketImpl似乎很奇怪,但我想java.net.SocketProxySelector的架构不允许它在Socket实例化时预先选择正确的impl。

I think you (or whoever is leading the current line of investigation on that Glassfish bug) may be going about this the wrong way: rather than trying to subvert the way the JRE selects socket impls and proxies, why not fix the default ProxySelector and/or OS native calls so that when Java does its standard queries for information about proxies, things don't break. 我认为你(或者那个领导当前调查Glassfish bug的人)可能会采用错误的方式:而不是试图破坏JRE选择套接字impls和代理的方式,为什么不修复默认的ProxySelector和/或OS本机调用,以便当Java对代理的信息进行标准查询时,事情不会中断。 I think the fix is in the guts of the proxy lookup process and classes, not higher up. 我认为修复是在代理查找过程和类的内核,而不是更高。


Maybe another way to ask what I'm asking is: if DefaultProxySelector can tell me the proxies to use for a socket connection, why doesn't Socket invoke that first to help it pick a sane implementation? 也许另一种方式来问我要问的是:如果DefaultProxySelector可以告诉我用于套接字连接的代理,为什么Socket不首先调用它来帮助它选择一个理智的实现?

I think the problem is that java.net.Socket supports several programming models. 我认为问题是java.net.Socket支持几种编程模型。 There's the obvious new Socket(host, port) constructor, but there's also a default constructor new Socket() which can be constructed first and then at some arbitrary time in the future can have its connect(SocketAddress) method called. 有明显的new Socket(host, port)构造函数,但是还有一个默认的构造函数new Socket()可以先构造,然后在将来的某个任意时间可以调用它的connect(SocketAddress)方法。

Since part of the criteria that a ProxySelector can use to determine whether a proxy should be used or not is the name of the remote host and remote port to be connected to (that is, the information supplied to connect ), the java.net.Socket constructor is too early to know whether a proxy will be needed or not. 由于ProxySelector可用于确定是否应使用代理的标准的一部分是要连接的远程主机和远程端口的名称(即,提供给connect的信息), java.net.Socket构造函数为时尚早,无法知道是否需要代理。

For whatever reason (can we just assume historical? / backwards compatibility reasons? :), the constructor for java.net.Socket the only place where the impl set, and, like I said, that is in some cases too early to tell whether a proxy will be needed or not. 无论出于何种原因(我们可以假设历史?/向后兼容性原因?:), java.net.Socket的构造函数是impl设置的唯一位置,并且,就像我说的那样,在某些情况下,判断是否为时尚早是否需要代理。

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

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