原理

通过 HTTP 拜访第三方获取 IP 的服务接口获取本机的外网 IP,例如:

  • http://checkip.amazonaws.com/
  • https://ipv4.icanhazip.com/
  • http://bot.whatismyipaddress.com/
  • 等等...

思考到这些第三方接口不肯定 100% 稳固,例如可能呈现下线、谬误、拜访超时或太慢的状况,所以不能仅依赖其中之一。

上面提供一种计划,并发拜访这些服务接口,并返回第一个胜利的后果,也就是其中最快的一个返回后果。

实现

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.concurrent.*;import java.util.regex.Pattern;public class ExternalIPUtil {    /**     * IP 地址校验的正则表达式     */    private static final Pattern IPV4_PATTERN = Pattern.compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");    /**     * 获取 IP 地址的服务列表     */    private static final String[] IPV4_SERVICES = {            "http://checkip.amazonaws.com/",            "https://ipv4.icanhazip.com/",            "http://bot.whatismyipaddress.com/"            // and so on ...    };    public static String get() throws ExecutionException, InterruptedException {        List<Callable<String>> callables = new ArrayList<>();        for (String ipService : IPV4_SERVICES) {            callables.add(() -> get(ipService));        }        ExecutorService executorService = Executors.newCachedThreadPool();        try {            // 返回第一个胜利获取的 IP            return executorService.invokeAny(callables);        } finally {            executorService.shutdown();        }    }    private static String get(String url) throws IOException {        try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL(url).openStream()))) {            String ip = in.readLine();            if (IPV4_PATTERN.matcher(ip).matches()) {                return ip;            } else {                throw new IOException("invalid IPv4 address: " + ip);            }        }    }}

线程池的 ExecutorService.invokeAny(callables) 办法用于并发执行多个线程,并拿到最快的执行胜利的线程的返回值,只有有一个执行胜利,其余失败的工作都会疏忽。然而如果全副失败,例如本机基本就没有连外网,那么就会抛出 ExecutionException 异样。

关注我的公众号