package org.springside.modules.utils.net;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Random;
import javax.net.ServerSocketFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springside.modules.utils.base.SystemPropertiesUtil;
import org.springside.modules.utils.collection.MapUtil;
import com.google.common.annotations.Beta;
/**
* 关于网络的工具类.
*
* 1. 获取Local Address
*
* 2. 查找空闲端口
*
* @author calvin
*
*/
@Beta
public class NetUtil {
private static Logger logger = LoggerFactory.getLogger(NetUtil.class);
public static final int PORT_RANGE_MIN = 1024;
public static final int PORT_RANGE_MAX = 65535;
private static final Random random = new Random();
private static InetAddress localAddress;
private static String localHost;
static {
initLocalAddress();
}
public static InetAddress getLocalAddress() {
return localAddress;
}
public static String getLocalHost() {
return localHost;
}
/**
* 测试端口是否空闲可用, from Spring SocketUtils
*/
public static boolean isPortAvailable(int port) {
try {
ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1,
InetAddress.getByName("localhost"));
serverSocket.close();
return true;
} catch (Exception ex) {
return false;
}
}
/**
* 从1024到65535, 随机找一个空闲端口 from Spring SocketUtils
*/
public static int findRandomAvailablePort() {
return findRandomAvailablePort(PORT_RANGE_MIN, PORT_RANGE_MAX);
}
/**
* 在范围里随机找一个空闲端口,from Spring SocketUtils.
*
* @throws IllegalStateException 最多尝试(maxPort-minPort)次,如无空闲端口,抛出此异常.
*/
public static int findRandomAvailablePort(int minPort, int maxPort) {
int portRange = maxPort - minPort;
int candidatePort;
int searchCounter = 0;
do {
if (++searchCounter > portRange) {
throw new IllegalStateException(
String.format("Could not find an available tcp port in the range [%d, %d] after %d attempts",
minPort, maxPort, searchCounter));
}
candidatePort = minPort + random.nextInt(portRange + 1);
} while (!isPortAvailable(candidatePort));
return candidatePort;
}
/**
* 从某个端口开始,递增直到65535,找一个空闲端口.
*
* @throws IllegalStateException 范围内如无空闲端口,抛出此异常
*/
public static int findAvailablePortFrom(int minPort) {
for (int port = minPort; port < PORT_RANGE_MAX; port++) {
if (isPortAvailable(port)) {
return port;
}
}
throw new IllegalStateException(
String.format("Could not find an available tcp port in the range [%d, %d]", minPort, PORT_RANGE_MAX));
}
/**
* 初始化本地地址
*/
private static void initLocalAddress() {
NetworkInterface nic = null;
// 根据命令行执行hostname获得本机hostname, 与/etc/hosts 中该hostname的第一条ip配置,获得ip地址
try {
localAddress = InetAddress.getLocalHost();
nic = NetworkInterface.getByInetAddress(localAddress);
} catch (Exception ignored) {
// NOSONAR
}
// 如果结果为空,或是一个loopback地址(127.0.0.1), 或是ipv6地址,再遍历网卡尝试获取
if (localAddress == null || nic == null || localAddress.isLoopbackAddress()
|| localAddress instanceof Inet6Address) {
InetAddress lookedUpAddr = findLocalAddressViaNetworkInterface();
// 仍然不符合要求,只好使用127.0.0.1
try {
localAddress = lookedUpAddr != null ? lookedUpAddr : InetAddress.getByName("127.0.0.1");
} catch (UnknownHostException ignored) {
// NOSONAR
}
}
localHost = IPUtil.toString(localAddress);
logger.info("localhost is {}", localHost);
}
/**
* 根据preferNamePrefix 与 defaultNicList的配置网卡,找出合适的网卡
*/
private static InetAddress findLocalAddressViaNetworkInterface() {
// 如果hostname +/etc/hosts 得到的是127.0.0.1, 则首选这块网卡
String preferNamePrefix = SystemPropertiesUtil.getString("localhost.prefer.nic.prefix",
"LOCALHOST_PREFER_NIC_PREFIX", "bond0.");
// 如果hostname +/etc/hosts 得到的是127.0.0.1, 和首选网卡都不符合要求,则按顺序遍历下面的网卡
String defaultNicList = SystemPropertiesUtil.getString("localhost.default.nic.list",
"LOCALHOST_DEFAULT_NIC_LIST", "bond0,eth0,em0,br0");
InetAddress resultAddress = null;
Map<String, NetworkInterface> candidateInterfaces = MapUtil.newHashMap();
// 遍历所有网卡,找出所有可用网卡,尝试找出符合prefer前缀的网卡
try {
for (Enumeration<NetworkInterface> allInterfaces = NetworkInterface.getNetworkInterfaces(); allInterfaces
.hasMoreElements();) {
NetworkInterface nic = allInterfaces.nextElement();
// 检查网卡可用并支持广播
try {
if (!nic.isUp() || !nic.supportsMulticast()) {
continue;
}
} catch (SocketException e) {
continue;
}
// 检查是否符合prefer前缀
String name = nic.getName();
if (name.startsWith(preferNamePrefix)) {
// 检查有否非ipv6 非127.0.0.1的inetaddress
resultAddress = findAvailableInetAddress(nic);
if (resultAddress != null) {
return resultAddress;
}
} else {
// 不是Prefer前缀,先放入可选列表
candidateInterfaces.put(name, nic);
}
}
for (String nifName : defaultNicList.split(",")) {
NetworkInterface nic = candidateInterfaces.get(nifName);
if (nic != null) {
resultAddress = findAvailableInetAddress(nic);
if (resultAddress != null) {
return resultAddress;
}
}
}
} catch (SocketException e) {
return null;
}
return null;
}
/**
* 检查有否非ipv6,非127.0.0.1的inetaddress
*/
private static InetAddress findAvailableInetAddress(NetworkInterface nic) {
for (Enumeration<InetAddress> indetAddresses = nic.getInetAddresses(); indetAddresses.hasMoreElements();) {
InetAddress inetAddress = indetAddresses.nextElement();
if (!(inetAddress instanceof Inet6Address) && !inetAddress.isLoopbackAddress()) {
return inetAddress;
}
}
return null;
}
}