【问题标题】:Unexpected HttpURLConnection connection behavior意外的 HttpURLConnection 连接行为
【发布时间】:2017-03-01 19:24:03
【问题描述】:

我正在将 Applet 转换为桌面应用程序,内部程序的服务之一包括一个 SOAP 客户端,旨在将信息发送到远程服务器。这是负责这个过程的一段代码:

package my.package.app.utils;

import my.package.app.main.MainClass;
import my.package.app.org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SoapClient {

    public static JSONObject callMethod(String path, String method, Object... args) {
        HttpURLConnection connection = null;
        String            data       = null;
        try {
            data = parseXML(method, args);

            MainClass.debug("Making http POST connections to : " + path);

            URL           u  = new URL(path);
            URLConnection uc = u.openConnection();
            connection = (HttpURLConnection) uc;

            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("SOAPAction", method);
            connection.setUseCaches(false);

            OutputStream out  = connection.getOutputStream();
            Writer       wout = new OutputStreamWriter(out);

            wout.write(data);
            wout.flush();
            wout.close();

            InputStream            in  = connection.getInputStream();
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder        db  = dbf.newDocumentBuilder();
            Document               doc = db.parse(in);

            doc.getDocumentElement().normalize();

            NodeList childs       = doc.getElementsByTagName("return");
            String   responseText = childs.item(0).getTextContent();

            if ("false".equals(responseText)) {
                return null;
            }

            JSONObject response = new JSONObject(responseText);

            in.close();
            return response;

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    private static String parseXML(String method, Object... args) {

        StringBuilder xml = new StringBuilder();

        xml.append(XMLTemplates.soapHeader);
        xml.append("<ns1:");
        xml.append(method);
        xml.append(">");

        for (int i = 0; i < args.length; i++) {

            String dataType = "xsi:type=\"xsd:string\"";

            xml.append("<param");
            xml.append(i);
            xml.append(" ");

            if (args[i] instanceof Integer) {
                dataType = "xsi:type=\"xsd:integer\"";
            }

            if (args[i] instanceof Double) {
                dataType = "xsi:type=\"xsd:decimal\"";
            }

            if (args[i] instanceof Boolean) {
                dataType = "xsi:type=\"xsd:boolean\"";
            }

            xml.append(dataType);
            xml.append(">");
            xml.append(String.valueOf(args[i]));
            xml.append("</param");
            xml.append(i);
            xml.append(">");
        }

        xml.append("</ns1:");
        xml.append(method);
        xml.append(">");

        xml.append(XMLTemplates.soapFooter);

        return xml.toString();
    }

}

当使用 callMethod 时,它会收到一个 path 字符串,其中的 URL 采用 HTTPS 协议,如下所示:“https://fileserver.myserver.net:9000/lf_soap_document_server_main.php”。

使用该 URL 运行 callMethod 时,applet 的 Java 控制台会显示以下内容:

18:15:45.476-DEBUG: Making http POST connections to : https://fileserver.myserver.net:9000/lf_soap_document_server_main.php
network: Connecting https://fileserver.myserver.net:9000/lf_soap_document_server_main.php with proxy=DIRECT
network: Connecting http://fileserver.myserver.net:9000/ with proxy=DIRECT

并且该过程成功完成。

但是,当从桌面应用程序版本执行具有相同信息的同一段代码时,会发生以下情况:

18:28:29.991-DEBUG: Making http POST connections to : https://fileserver.myserver.net:9000/lf_soap_document_server_main.php
network: Connecting https://fileserver.myserver.net:9000/lf_soap_document_server_main.php with proxy=DIRECT
network: Connecting socket://fileserver.myserver.net:9000 with proxy=DIRECT
java.net.ConnectException: Connection timed out: connect

当调用 OutputStream out = connection.getOutputStream(); 时会出现问题。请注意,日志消息将 URL 的协议从 https:// 更改为 socket://,并且似乎还删除了最后一个斜杠 (/)。

这是完全的例外:

java.net.ConnectException: Connection timed out: connect
  at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
  at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
  at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
  at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
  at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
  at java.net.PlainSocketImpl.connect(Unknown Source)
  at java.net.SocksSocketImpl.connect(Unknown Source)
  at java.net.Socket.connect(Unknown Source)
  at sun.security.ssl.SSLSocketImpl.connect(Unknown Source)
  at sun.net.NetworkClient.doConnect(Unknown Source)
  at sun.net.www.http.HttpClient.openServer(Unknown Source)
  at sun.net.www.http.HttpClient.openServer(Unknown Source)
  at sun.net.www.protocol.https.HttpsClient.<init>(Unknown Source)
  at sun.net.www.protocol.https.HttpsClient.New(Unknown Source)
  at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection$6.run(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection$6.run(Unknown Source)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.AccessController.doPrivilegedWithCombiner(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
  at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection.access$100(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection$8.run(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection$8.run(Unknown Source)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.security.AccessController.doPrivilegedWithCombiner(Unknown Source)
  at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
  at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
  at my.package.app.utils.SoapClient.callMethod(SoapClient.java:42)
  at my.package.app.services.scanner.LFScanner$SoapUpload.run(LFScanner.java:746)

桌面应用程序使用 Jetty 创建到安全 websocket 服务器的连接,并在用户的机器中创建本地 websocket 服务器,但是它们都没有与发生问题的 SoapClient 类有业务。

我已经测试了为连接设置 5 分钟超时并读取到 HttpURLConnection 实例,但问题在不到一分钟的时间内一直在桌面应用程序上发生。

我使用 1.8.0_111-b14 作为运行时在 Windows 10 机器上执行程序(小程序和桌面应用程序),64 位。

我需要知道这段代码是否有任何问题,或者这里是否缺少设置以防止这种情况发生。

非常感谢任何帮助。

谢谢。

【问题讨论】:

  • 您是否使用代理从桌面访问互联网?如果是这样,您的 Java 代码也需要使用它。
  • 不,我没有使用代理。
  • 还是防火墙?你能在网络浏览器中打开网址吗?
  • 是的。我可以毫无问题地在浏览器上打开 URL。

标签: java sockets https applet network-protocols


【解决方案1】:

我在一个孤立的程序中测试了有问题的代码段,只使用了重现所需的最少代码,发现问题实际上出在服务器端,即为 URL 提供 SOAP 服务的服务器端。到目前为止,大多数测试在其他程序中都可以正常工作,但发现 SOAP url 在其他程序中也存在可用性问题,而不仅仅是 Java,这通常解释了这个问题。

用于连接和使用 SOAP 服务的代码没有问题,问题出在目标 URL 上。

我仍然不知道 Java 控制台在尝试访问 SOAP URL 时在调试信息中显示不同协议的原因,但无论如何(目前)这似乎不是问题。

【讨论】:

    猜你喜欢
    • 2016-02-28
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 2021-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多