osociety / network_tools

Networking Tools library which can help you discover open ports, devices on subnet and many other things.
https://pub.dev/packages/network_tools
BSD 3-Clause "New" or "Revised" License
42 stars 13 forks source link

Add support for checking list of ip addresses #180

Closed kengu closed 6 months ago

kengu commented 7 months ago

Rationale My use case is presence detection ("arrive and leave home"). A service maintains a list of discovered devices on given subnet. Since ARP data is cached for some time after a device has disconnected from the network, I use this to determine if the device is available (respons to ping) or not (still in ARP cache). When a device is no longer in the ARP cache, the device is removed from the service.

Adding a truly new device to a network does not happen that often. Since the typical number of devices on a home network is much less then 256, and subnet-based discovery is a resource heavy operation for typical home network (default subnet is typically 256), it makes sense to implement an optimization for detecting if a specific (already discovered) device is available or not when the number of devices is much smaller than the subnet used. This would allow me to check availability more often (on a small number of devices) than the full subnet scan that will detect truly new devices.

Instead of implementing this my self by reimplementing the excellent solution using isolates already in place for subnet-based discovery, I would like to propose the following feature to be added to network_tools.

Proposal Add a optional argument hostIds to existing discovery methods in HostScanner that subnet is evaluated against using hostId. This adds to existing logic without breaking it. This is how the method HostScanner.getAllPingableDevices signature would look like

 static Stream<ActiveHost> getAllPingableDevices(
    String subnet, {
    int firstHostId = defaultFirstHostId,
    int lastHostId = defaultLastHostId,
    List<int>? hostIds,
    int timeoutInSeconds = 1,
    ProgressCallback? progressCallback,
    bool resultsInAddressAscendingOrder = true,
  })

I'm already working on the implementation in my fork. If you would like to add this feature to network_tools, I'll make a pull-request when I'm done testing.

guyluz11 commented 7 months ago

In general I think there should also be an option to unfunctions without isolate.

kengu commented 7 months ago

In general I think there should also be an option to unfunctions without isolate.

Did you mean adding hostIds to all these functions?

If yes, I've done that in this PR now.

The tests are also extended to test for include/exclude using hostIds argument

guyluz11 commented 7 months ago

*run functions without isolate.

kengu commented 7 months ago

I've tested the implementation with my use-case using this pseduo-algorithm

fullEvery := 5 min
liveEvery := 1 min

start with full scan 
    for all discovered network devices
        store state (hostId, name, address, vendor, availability)

do liveEvery period
    if fullEvery period reached
        do full scan and update states
    else 
        do live scan and update states

My test network have around 20 connected devices with subnet /24 (which leads to 256 address checks per full scan). A full scan on my dev-machine (macos) takes about 30s using HostScanner.getAllPingableDevicesAsync, and a scan of only discovered devices takes about 20s (the sharding of addresses in chuncks of 51 is unmodified). The difference is in the amount CPU time the scan takes which is significant (number of ping-instances is significantly reduced from 51 per shard). This effect will of course be inverse proportional with the number of unike addresses on the network, the more devices the network have, the less reduction in CPU time you will see.

If the goal was to reduce the scan time, we would need to also change the sharding mechanism. This could be done by modifying scanRangeForIsolate in HostScanner.getAllPingableDevicesAsync to take into account the number of items in hostIds. Since I'm looking for minimizing the overall system load by spreading out the scan over time, this is not something I've looked into (yet). The solution could be as simple as increasing the shard size by implementing an inverse relationship with number of items in hostIds, making more of them fit into one or more shards depending on how much you would like to "pack" the scan.