1. Introduction

In networking, retrieving the list of IP addresses connected within the same network (subnet) is essential for tasks such as network monitoring and device administration. Additionally, this helps identify active devices in a specific IP range and ensures they are reachable.

In this tutorial, we’ll explore various methods in Java to scan and retrieve a list of IP addresses within the same subnet. We’ll cover solutions using Java’s InetAddress class and enhancements using Java 8 Stream API. Finally, we’ll demonstrate more advanced subnet handling with the Apache Commons Net library.

2. Understanding IP Addresses and Subnets

An IP address uniquely identifies devices on a network, while a subnet groups a range of IP addresses together. Subnets allow networks to be divided into smaller, more manageable blocks, helping to improve performance and security.

A subnet is typically represented by an IP address and a subnet mask (for example, 192.168.1.0/24). The subnet mask defines which portion of the IP address represents the network and which portion identifies individual hosts.

For instance, the subnet 192.168.1.0/24 covers all addresses from 192.168.1.1 to 192.168.1.254. In this case, the first three octets (192.168.1) represent the network, and the last octet can be any number from 1 to 254, identifying the individual hosts.

The subnet mask 255.255.255.0 indicates that the first three sections of the IP address represent the network, while the last section varies for the host.

Let’s see how to determine the subnet dynamically in Java:

private String getSubnet() throws UnknownHostException {
    InetAddress localHost = InetAddress.getLocalHost();
    byte[] ipAddr = localHost.getAddress();
    return String.format("%d.%d.%d", (ipAddr[0] & 0xFF), (ipAddr[1] & 0xFF), (ipAddr[2] & 0xFF));
}

This method retrieves the local machine’s IP address, and we extract the subnet from the first three octets. The code dynamically calculates the subnet based on the environment, allowing it to adapt to different network configurations.

3. Using Java’s InetAddress Class

One of the simplest ways to check the reachability of devices in a network is by using Java’s InetAddress class. Furthermore, this class allows us to verify whether a device at a specific IP address is reachable within a given timeout period.

Once we determine the subnet dynamically, we can loop through the possible IP addresses within the subnet by appending numbers from 1 to 254 to the base address:

@Test
public void givenSubnet_whenScanningForDevices_thenReturnConnectedIPs() throws Exception {
    String subnet = getSubnet();
    List<String> connectedIPs = new ArrayList<>();

    for (int i = 1; i <= 254; i++) {
        String ip = subnet + "." + i;
        if (InetAddress.getByName(ip).isReachable(100)) {
            connectedIPs.add(ip);
        }
    }

    assertFalse(connectedIPs.isEmpty());
}

For each IP, we use the InetAddress.getByName() method to create an InetAddress object. Then, we check if it’s reachable using the isReachable() method. We add the IP address to the list if the device is reachable.

The list of IP addresses will vary depending on the devices currently connected to the same network.

3.1. Streamlining Subnet Scanning with Java 8 Stream API

Java 8 introduced the Stream API, which allows us to process collections and arrays in a concise and functional manner. Moreover, we can use this feature to perform subnet scanning in a streamlined way:

@Test
public void givenSubnet_whenUsingStream_thenReturnConnectedIPs() throws UnknownHostException {
    String subnet = getSubnet();

    List<String> connectedIPs = IntStream.rangeClosed(1, 254)
            .mapToObj(i -> subnet + "." + i)
            .filter(ip -> {
                try {
                    return InetAddress.getByName(ip).isReachable(100);
                } catch (Exception e) {
                    return false;
                }
            })
            .toList();

    assertFalse(connectedIPs.isEmpty());
}

Here, we use IntStream.rangeClosed(1, 254) to generate the range of possible IP addresses. We then use mapToObj() to append the generated number to the dynamically retrieved subnet and filter() to check if each IP is reachable.

While this doesn’t introduce new networking capabilities, it demonstrates how we can organize and streamline the solution using the Stream API, a powerful addition introduced in Java 8.

4. Advanced Subnet Handling with Apache Commons Net Library

For more advanced subnet management, we can use the Apache Commons Net library, which provides utilities to handle subnets easily. A use case for this library involves checking for open ports (such as port 80) on devices within a subnet using TelnetClient, a subclass of SocketClient provided by the Apache Commons Net library:

@Test
public void givenSubnet_whenCheckingForOpenPorts_thenReturnDevicesWithOpenPort() throws UnknownHostException {
    SubnetUtils utils = new SubnetUtils(getSubnet() + ".0/24");
    int port = 80;
    List<String> devicesWithOpenPort = Arrays.stream(utils.getInfo().getAllAddresses())
            .filter(ip -> {
                TelnetClient telnetClient = new TelnetClient();
                try {
                    telnetClient.setConnectTimeout(100);
                    telnetClient.connect(ip, port);
                    return telnetClient.isConnected();
                } catch (Exception e) {
                    return false;
                } finally {
                    try {
                        if (telnetClient.isConnected()) {
                            telnetClient.disconnect();
                        }
                    } catch (IOException ex) {
                        System.err.println(ex.getMessage());
                    }
                }
            })
            .toList();

    assertFalse(devicesWithOpenPort.isEmpty());
}

In this example, SubnetUtils generates all valid IP addresses within the subnet (such as 192.168.1.0/24). For each IP address, we attempt to connect to port 80 using the TelnetClient.connect() method. When the connection succeeds, the system adds the IP address to the list of devices with open ports. It then closes the connection in the finally block using telnetClient.disconnect() to ensure proper resource management.

5. Conclusion

In this tutorial, we’ve explored different ways to scan and retrieve a list of IP addresses connected within the same subnet using Java.

We used the InetAddress class for simplicity, Java 8’s Stream API for concise functional programming, and the Apache Commons Net library for more robust subnet handling and advanced tasks like port scanning.

As always, the complete code samples for this article can be found over on GitHub.