Apache Axis 1.4 SOAP请求SSLSocket握手超时问题求助
问题分析
从你提供的错误栈和描述来看,这个问题的核心是Axis 1.4默认的JSSESocketFactory没有为SSL握手阶段设置超时。你配置的30秒超时只作用于Socket的读写阶段,而SSL握手是在Socket连接建立后单独执行的原生操作,默认会使用JDK的全局SSL超时(通常是15分钟左右,和你遇到的1000秒吻合),导致系统长时间挂起才抛出异常。
下面给你几个可行的解决方案,按推荐优先级排序:
方案1:利用Java 7+的原生SSL握手超时API(最简洁)
如果你的项目已经升级到Java 7或更高版本,SSLSocket类原生提供了setHandshakeTimeout方法,可以直接在握手前设置超时时间。你需要重写Axis的JSSESocketFactory,在创建Socket后、触发握手前插入超时配置:
1.1 自定义SocketFactory类
public class TimeoutEnabledJSSESocketFactory extends JSSESocketFactory { // 自定义握手超时时间(毫秒) private static final int HANDSHAKE_TIMEOUT_MS = 30000; @Override public Socket create(Socket tunnel, String host, int port, boolean autoClose) throws IOException { // 调用父类方法创建SSLSocket Socket socket = super.create(tunnel, host, port, autoClose); if (socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket) socket; // 设置握手超时为30秒 sslSocket.setHandshakeTimeout(HANDSHAKE_TIMEOUT_MS, TimeUnit.MILLISECONDS); } return socket; } // 同时重写不带tunnel的create方法,覆盖所有场景 @Override public Socket create(String host, int port, StringBuffer otherHeaders, Boolean useFullURL) throws IOException { Socket socket = super.create(host, port, otherHeaders, useFullURL); if (socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket) socket; sslSocket.setHandshakeTimeout(HANDSHAKE_TIMEOUT_MS, TimeUnit.MILLISECONDS); } return socket; } }
1.2 替换Axis的默认SocketFactory
在你的Axis客户端配置文件(axis.xml)中,将HTTPS传输的socketFactory替换为自定义类:
<transport name="https" pivot="java:org.apache.axis.transport.http.HTTPSender"> <parameter name="socketFactory" value="com.yourcompany.TimeoutEnabledJSSESocketFactory"/> <!-- 保留你原来的其他超时配置 --> <parameter name="http.socket.timeout" value="30000"/> <parameter name="http.connection.timeout" value="30000"/> </transport>
方案2:JDK版本低于7?用线程中断强制超时
如果你的项目还在使用Java 6或更早版本,没有原生的握手超时API,可以通过FutureTask将握手操作放到子线程中,超时后中断线程并关闭Socket:
2.1 封装超时工具方法
public static SSLSocket createSSLSocketWithHandshakeTimeout( SocketFactory sslFactory, Socket tunnel, String host, int port, boolean autoClose, int timeoutMs) throws Exception { FutureTask<SSLSocket> handshakeTask = new FutureTask<>(() -> { SSLSocket sslSocket = (SSLSocket) sslFactory.createSocket(tunnel, host, port, autoClose); sslSocket.startHandshake(); // 手动触发握手 return sslSocket; }); Thread handshakeThread = new Thread(handshakeTask); handshakeThread.start(); try { // 等待超时时间内完成握手 return handshakeTask.get(timeoutMs, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // 超时后中断线程并关闭Socket handshakeThread.interrupt(); try { if (!handshakeTask.isCancelled()) { handshakeTask.get().close(); } } catch (Exception ex) { // 忽略关闭时的异常 } throw new IOException("SSL handshake timed out after " + timeoutMs + "ms", e); } }
2.2 替换原有的Socket创建逻辑
把你代码中的sslFactory.createSocket(...)调用替换为工具方法:
// 原代码:sslSocket = sslFactory.createSocket(tunnel, host, port, true); // 替换为: sslSocket = createSSLSocketWithHandshakeTimeout(sslFactory, tunnel, host, port, true, 30000);
注意:线程中断对原生SSL握手的中断效果可能存在差异,但大多数场景下会触发
SocketException,达到超时断开的目的。
方案3:补充Axis的全局超时配置(辅助验证)
确保你已经完整配置了Axis的所有超时参数,避免其他环节的超时遗漏:
- 在客户端Stub代码中设置:
YourServiceStub stub = new YourServiceStub(); stub.setTimeout(30000); // 设置调用超时 - 在
axis.xml中补充HTTP传输的超时:<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"> <parameter name="http.socket.timeout" value="30000"/> <parameter name="http.connection.timeout" value="30000"/> </transport>
这些配置主要作用于Socket的读写阶段,无法直接解决SSL握手超时,但能确保整个调用链路的超时一致性。
内容的提问来源于stack exchange,提问作者Eldon Hipolito

