在Java中是否有一种特定的数据类型可以存储IP地址?我有一个具体的功能需求:
- 给定一个IP范围和一个IP,如果该IP在范围内则返回true,否则返回false。例如:范围为10.10.10.1-10.10.11.255,IP为10.10.10.192,则应返回true。
我知道有java.net.inetaddress
,但我相信它不能提供这个功能。有什么建议吗?
java.net.InetAddress
或其子类,并编写自定义比较器和范围类:
getByName
会导致DNS访问。如果您想避免DNS的惩罚,您可以查看Guava的com.google.common.net.InetAddresses
帮助类。public enum InetAddressComparator implements Comparator<InetAddress> {
INSTANCE;
public int compare(InetAddress first, InetAddress second) {
byte[] firstBytes = first.getAddress();
byte[] secondBytes = second.getAddress();
if (firstBytes.length != secondBytes.length) {
throw new IllegalArgumentException("Cannot compare IPv4 and IPv6 addresses");
}
// getAddress returns bytes in network byte order:
// the least significant byte is at the last index
for (int i = firstBytes.length - 1; i >= 0; i--) {
// translate the byte to an int with only the last 8 bits set,
// effectively treating it as unsigned
int a = firstBytes[i] & 0xff;
int b = secondBytes[i] & 0xff;
if (a < b) {
return -1;
} else if (a > b) {
return 1;
}
}
return 0;
}
}
public class Range<T> {
private T lower;
private T upper;
private Comparator<T> comparator;
public Range(T lower, T upper, Comparator<T> comparator) {
if (comparator.compare(lower, upper) <= 0) {
this.lower = lower;
this.upper = upper;
} else {
this.lower = upper;
this.upper = lower;
}
this.comparator = comparator;
}
public boolean contains(T element) {
return comparator.compare(lower, element) <= 0 &&
comparator.compare(upper, element) >= 0;
}
}
public class Main {
public static void main(String[] args) throws Exception {
InetAddress start = InetAddress.getByName("10.10.10.1");
InetAddress end = InetAddress.getByName("10.10.11.255");
InetAddress test = InetAddress.getByName("10.10.10.192");
assert InetAddressComparator.INSTANCE.compare(start, test) == -1;
assert InetAddressComparator.INSTANCE.compare(end, test) == 1;
assert InetAddressComparator.INSTANCE.compare(test, test) == 0;
assert new Range<InetAddress>(start, end, InetAddressComparator.INSTANCE)
.contains(test);
}
}
IP(IPv4)是32位(与Java中的int大小相同)。由于您希望使用无符号整数进行比较(如果需要支持128.0.0.0以上的IP),因此需要改用long。
10.10.10.1 is: (10 << 24) + (10 << 16) + (10 << 8) + 1 = 168430081
10.10.11.255 is: (10 << 24) + (10 << 16) + (11 << 8) + 255 = 168430591
10.10.10.192 is: (10 << 24) + (10 << 16) + (10 << 8) + 192 = 168430272
由于 168430081 <= 168430272 && 168430272 <= 168430591
,(换句话说,168430272在168430081和168430272之间),您的IP位于该范围内。
int a = (ipv4 >> 24) & 127;
sb.append(a);
a = (ipv4 >> 16) & 255;
sb.append("." + a);
a = (ipv4 >> 8) & 255;
sb.append("." + a);
a = ipv4 & 255;
sb.append("." + a);
return sb.toString();`
- jbarrameda如果您的IP地址范围可以用常见的CIDR格式表示,您可以使用Apache Common的SubnetUtils
。
SubnetUtils.SubnetInfo subnet = new SubnetUtils("192.168.0.3/31");
return subnet.isInRange(testIpAddress);
int
类型的值。将IP地址转换为数字,然后可以使用数字操作。210.210.210.1 is: (210 << 24) + (210 << 16) + (210 << 8) + 1 = -757935615
210.210.211.255 is: (210 << 24) + (210 << 16) + (211 << 8) + 255 = -757935105
210.210.210.192 is: (210 << 24) + (210 << 16) + (210 << 8) + 192 = -757935424
最后一个IP地址在范围内,因为-757935615 <= -757935424 && -757935424 <= -757935105。
试试这个:
import static org.junit.Assert.*;
import org.junit.Test;
import java.math.BigInteger;
import java.net.*;
class IpRange {
IpRange(InetAddress from, InetAddress to) {
if(!from.getClass().equals(to.getClass())) throw new RuntimeException("different versions of ip address!");
this.from = new BigInteger(from.getAddress());
this.to = new BigInteger(to.getAddress());
}
boolean isInRange(InetAddress inetAddress) {
BigInteger bigInteger = new BigInteger(inetAddress.getAddress());
return !(from.compareTo(bigInteger) == 1 || bigInteger.compareTo(to) == 1);
}
final BigInteger from, to;
}
public class IpRangeTestCase {
@Test public void testInRange() throws UnknownHostException {
InetAddress from = InetAddress.getByAddress(new byte[] { 10, 10, 10, 1 });
InetAddress x = InetAddress.getByAddress(new byte[] { 10, 10, 10, 42 });
InetAddress to = InetAddress.getByAddress(new byte[] { 10, 10, 10, (byte) 192 });
IpRange ipRange = new IpRange(from, to);
assertTrue(ipRange.isInRange(from));
assertTrue(ipRange.isInRange(x));
assertTrue(ipRange.isInRange(to));
InetAddress toSmall = InetAddress.getByAddress(new byte[] { 10, 10, 9, 1 });
assertFalse(ipRange.isInRange(toSmall));
InetAddress toBig = InetAddress.getByAddress(new byte[] { 10, 10, 10, (byte) 193 });
assertFalse(ipRange.isInRange(toBig));
InetAddress fromv6=InetAddress.getByAddress(new byte[] {(byte)0xfe,(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,(byte)0xb3,(byte)0xff,(byte)0xfe,0x1e,(byte)0x83,0x20});
InetAddress xv6=InetAddress.getByAddress(new byte[] {(byte)0xfe,(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,(byte)0xb3,(byte)0xff,(byte)0xfe,0x1e,(byte)0x83,0x29});
InetAddress tov6=InetAddress.getByAddress(new byte[] {(byte)0xfe,(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,(byte)0xb3,(byte)0xff,(byte)0xfe,0x1e,(byte)0x83,0x40});
IpRange ipRangev6 = new IpRange(fromv6, tov6);
assertTrue(ipRangev6.isInRange(xv6));
}
@Test (expected=RuntimeException.class) public void testInRangeThrows() throws UnknownHostException {
InetAddress v4 = InetAddress.getByAddress(new byte[] { 10, 10, 10, 1 });
InetAddress v6=InetAddress.getByAddress(new byte[] {(byte)0xfe,(byte)0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,(byte)0xb3,(byte)0xff,(byte)0xfe,0x1e,(byte)0x83,0x29});
new IpRange(v4, v6);
}
}
只需使用Java的原始数据类型long
testIp > minIp && testIp < maxIp
以查看它是否在范围内。有关如何实现转换的详细信息,请参见@user1的答案。 - bezmax