使用Apache HttpClient 4.5.x时遇Address family not supported by protocol错误
问题:Apache HttpClient 4.5 IPv6连接抛出SocketException异常
我开发了一个测试HTTPS连通性的小型应用,使用Apache HttpClient 4.5创建HttpClient并发起HttpGet请求。应用在IPv4环境下运行正常,但连接IPv6时抛出如下异常:
2023-10-05T12:04:38.906Z java.net.SocketException: Address family not supported by protocol 2023-10-05T12:04:38.906Z at java.base/sun.nio.ch.Net.connect0(Native Method) 2023-10-05T12:04:38.906Z at java.base/sun.nio.ch.Net.connect(Net.java:579) 2023-10-05T12:04:38.907Z at java.base/sun.nio.ch.Net.connect(Net.java:568) 2023-10-05T12:04:38.907Z at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:588) 2023-10-05T12:04:38.907Z at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) 2023-10-05T12:04:38.907Z at java.base/java.net.Socket.connect(Socket.java:633) 2023-10-05T12:04:38.907Z at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:368) 2023-10-05T12:04:38.907Z at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142) 2023-10-05T12:04:38.907Z at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376) 2023-10-05T12:04:38.907Z at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393) 2023-10-05T12:04:38.908Z at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) 2023-10-05T12:04:38.908Z at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) 2023-10-05T12:04:38.908Z at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) 2023-10-05T12:04:38.908Z at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) 2023-10-05T12:04:38.908Z at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) 2023-10-05T12:04:38.908Z at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) 2023-10-05T12:04:38.908Z at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) 2023-10-05T12:04:38.908Z at com.ibm.proxy.util.Connection.doTestConnection(Connection.java:123)
TestConnection方法代码
private boolean doTestConnection(String host, ProtocolPojo protocol) { boolean connected = false; String serverURL = "https://" + host; HttpGet getMethodProxyRequest = new HttpGet(serverURL); HttpClientBuilder clientBuilder = HttpClients.custom(); CloseableHttpClient httpClient = clientBuilder .setSSLSocketFactory(protocol.getSecureProtocolSocketFactory()) .build(); CloseableHttpResponse proxyResponse = null; try { RequestConfig reqConf = RequestConfig.custom() .setLocalAddress(InetAddress.getLocalHost()) .setAuthenticationEnabled(false).setRedirectsEnabled(false) .setConnectTimeout(10000).setSocketTimeout(15000).build(); getMethodProxyRequest.setConfig(reqConf); proxyResponse = httpClient.execute(getMethodProxyRequest); } catch (UnknownHostException e) { logException(e); logMsgInfo( "Proxy: not able to resolve localhost: " + e.getMessage()); } catch (IOException e) { logException(e); } finally { if (proxyResponse != null && proxyResponse.getStatusLine() != null) { logMsgInfo( "Proxy: " + proxyResponse.getStatusLine().toString()); logMsgInfo("Proxy: " + proxyResponse.getStatusLine().getStatusCode()); connected = (proxyResponse.getStatusLine() .getStatusCode() == 200); } try { httpClient.close(); } catch (IOException e) { logException(e); } } return connected; }
ProtocolPojo类代码
public class ProtocolPojo { private String schema; private int port; private StrictSSLProtocolSocketFactory secureProtocolSocketFactory; public ASMProtocol(String schema, StrictSSLProtocolSocketFactory secureProtocolSocketFactory, int port) { this.schema = schema; this.port = port; this.secureProtocolSocketFactory = secureProtocolSocketFactory; } public String getSchema() { return schema; } public int getPort() { return port; } /** * org.apache.http.conn.ssl.SSLConnectionSocketFactory - that * can be used to verify the host certificate that is * recived. */ public SSLConnectionSocketFactory getSecureProtocolSocketFactory() { return secureProtocolSocketFactory.getSSLConnectionSocketFactory(); } }
已通过浏览器手动测试IPv6,确认可正常访问。怀疑问题出在Apache HttpClient 4.5中,请问是否有处理该异常的方法?这是否是环境问题?
解决方案
核心问题定位
代码中RequestConfig.custom().setLocalAddress(InetAddress.getLocalHost())强制绑定了本地IPv4地址。当目标是IPv6地址时,用IPv4本地地址去连接IPv6服务,会触发Address family not supported by protocol异常——因为IPv4和IPv6属于不同的地址族,无法跨族建立连接。
修复方法
- 最简修复:直接移除
setLocalAddress(InetAddress.getLocalHost())配置,让HttpClient自动匹配目标地址类型选择对应的本地地址族:
RequestConfig reqConf = RequestConfig.custom() .setAuthenticationEnabled(false) .setRedirectsEnabled(false) .setConnectTimeout(10000) .setSocketTimeout(15000) .build();
- 动态绑定本地地址(如果必须绑定):如果业务需要绑定特定本地地址,需根据目标主机的地址类型动态选择IPv4或IPv6本地地址:
InetAddress localAddress = null; // 解析目标主机地址,判断地址类型 InetAddress[] targetAddrs = InetAddress.getAllByName(host); for (InetAddress addr : targetAddrs) { if (addr instanceof Inet6Address) { // 查找可用的本地IPv6地址(非回环) Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); Enumeration<InetAddress> inetAddrs = ni.getInetAddresses(); while (inetAddrs.hasMoreElements()) { InetAddress localAddr = inetAddrs.nextElement(); if (localAddr instanceof Inet6Address && !localAddr.isLoopbackAddress()) { localAddress = localAddr; break; } } if (localAddress != null) break; } break; } else { // 目标是IPv4,绑定本地IPv4地址 localAddress = InetAddress.getLocalHost(); break; } } RequestConfig reqConf = RequestConfig.custom() .setLocalAddress(localAddress) .setAuthenticationEnabled(false) .setRedirectsEnabled(false) .setConnectTimeout(10000) .setSocketTimeout(15000) .build();
环境排查补充
若修复代码后仍异常,检查以下内容:
- JVM参数:确认未设置
java.net.preferIPv4Stack=true,该参数会强制JVM优先使用IPv4,禁用IPv6支持。 - 系统网络配置:验证主机已正确配置IPv6地址,且网络可路由,防火墙未拦截IPv6流量。
- HttpClient版本:确保使用的是Apache HttpClient 4.5的稳定版本,避免依赖包冲突导致的IPv6支持异常。
内容的提问来源于stack exchange,提问作者Suman Mummaneni
相关产品推荐
相关产品推荐

