Тип данных для хранения IP-адресов - PullRequest
12 голосов
/ 30 декабря 2011

Существует ли определенный тип данных для хранения IP-адресов в Java?У меня есть одно конкретное требование к функциональности:

  1. При заданном диапазоне IP и IP верните true, если он попадает в него, в противном случае - false.Например: диапазон 10.10.10.1-10.10.11.255 и IP 10.10.10.192 должны возвращать true.

Я знаю java.net.inetaddress, но я считаю, что это не дает мне этой функциональности.Есть идеи?

Ответы [ 7 ]

13 голосов
/ 30 декабря 2011

Я бы использовал java.net.InetAddress или один из его подклассов и написал бы собственный компаратор плюс класс диапазона:

  • Поддерживать и отлаживать проще с явным типом InetAddress, а не только с longs: Ваш отладчикна самом деле будет отображаться «10.10.10.1», а не «168430081»
  • IPv6 не является проблемой или может быть реализован без особых дополнительных хлопот.

Один недостаток InetAddress заключается в том, чтоgetByName приводит к доступу к DNS.Возможно, вы захотите взглянуть на вспомогательный класс Guava * com.google.common.net.InetAddresses, если хотите избежать штрафа за DNS.

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);
  }
}
13 голосов
/ 30 декабря 2011

IP (IPv4) составляет 32 бита (такого же размера, как int в Java).Поскольку вы хотите делать сравнения, используя целые числа без знака (если вам нужно поддерживать IP выше 128.0.0.0), вам нужно вместо этого использовать 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 находится в диапазоне.

3 голосов
/ 08 марта 2016

Если ваш диапазон IP-адресов может быть представлен в общем формате CIDR , вы можете использовать Apache Common's SubnetUtils.

SubnetUtils.SubnetInfo subnet = new SubnetUtils("192.168.0.3/31");
return subnet.isInRange(testIpAddress);
3 голосов
/ 30 декабря 2011

Для IPv4 я бы использовал int значения. Преобразуйте IP-адрес в число, и вы можете использовать числовые операции.

Если вы хотите сделать сравнения, которые находятся в диапазоне от 127.x.x.x (loop back lan) до 128.x.x.x, что маловероятно, но, скажем, у вас есть, вы можете перевернуть верхний бит, и сравнение диапазонов все равно будет работать.

Используя пример @ user1. проверьте, находится ли 210.210.210.192 между 210.210.210.1 и 210.210.211.255

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 </p>

2 голосов
/ 30 декабря 2011

попробуйте это:

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);
    }
}
2 голосов
/ 30 декабря 2011

Преобразование IP-адреса в длинное целое. См. Учитывая список IP-адресов, как вы находите min, max? для хорошего обсуждения той же проблемы.

0 голосов
/ 30 декабря 2011

Просто используйте примитив Java long

...