Skip to content

Commit

Permalink
Merge pull request #113 from osociety/bug_fixes
Browse files Browse the repository at this point in the history
Bug fixes
  • Loading branch information
git-elliot authored Aug 30, 2023
2 parents 3e1ccf6 + 50685db commit af68c3a
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 60 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
# separate terms of service, privacy policy, and support
# documentation.

name: Dart
name: Flutter

on:
push:
branches: [ "main" ]
paths: ['network_tools_flutter/**']
pull_request:
branches: [ "main", "dev" ]
branches: [ "main", "dev-flutter" ]
paths: ['network_tools_flutter/**']

concurrency:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:

jobs:
publish:

runs-on: ubuntu-latest

steps:
Expand Down
3 changes: 2 additions & 1 deletion network_tools/example/host_scan.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ void main() {
'${DateFormat.Hms().format(record.time)}: ${record.level.name}: ${record.loggerName}: ${record.message}',
);
});
final log = Logger("host_scan_example");

const String address = '192.168.1.1';
// or You can also get address using network_info_plus package
Expand All @@ -17,7 +18,7 @@ void main() {

// You can set [firstHostId] and scan will start from this host in the network.
// Similarly set [lastHostId] and scan will end at this host in the network.
final stream = HostScanner.getAllPingableDevices(
final stream = HostScanner.getAllPingableDevicesAsync(
subnet,
// firstHostId: 1,
// lastHostId: 254,
Expand Down
2 changes: 2 additions & 0 deletions network_tools/lib/network_tools.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/// Network tools base library
library network_tools;

//TODO: add dartdocs
Expand All @@ -7,4 +8,5 @@ export 'src/models/active_host.dart';
export 'src/models/callbacks.dart';
export 'src/models/mdns_info.dart';
export 'src/models/open_port.dart';
export 'src/models/sendable_active_host.dart';
export 'src/port_scanner.dart';
74 changes: 43 additions & 31 deletions network_tools/lib/src/host_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:dart_ping/dart_ping.dart';
import 'package:isolate_manager/isolate_manager.dart';
import 'package:network_tools/src/models/active_host.dart';
import 'package:network_tools/src/models/callbacks.dart';
import 'package:network_tools/src/models/sendable_active_host.dart';
import 'package:network_tools/src/network_tools_utils.dart';
import 'package:network_tools/src/port_scanner.dart';

Expand All @@ -29,12 +30,31 @@ class HostScanner {
int timeoutInSeconds = 1,
ProgressCallback? progressCallback,
bool resultsInAddressAscendingOrder = true,
}) async* {
final stream = getAllSendablePingableDevices(subnet, firstHostId: firstHostId, lastHostId: lastHostId,
timeoutInSeconds: timeoutInSeconds, progressCallback: progressCallback, resultsInAddressAscendingOrder: resultsInAddressAscendingOrder,);
await for (final sendableActiveHost in stream){
final activeHost = ActiveHost.fromSendableActiveHost(sendableActiveHost: sendableActiveHost);

await activeHost.resolveInfo();

yield activeHost;
}
}
/// Same as [getAllPingableDevices] but can be called or run inside isolate.
static Stream<SendableActiveHost> getAllSendablePingableDevices(
String subnet, {
int firstHostId = defaultFirstHostId,
int lastHostId = defaultLastHostId,
int timeoutInSeconds = 1,
ProgressCallback? progressCallback,
bool resultsInAddressAscendingOrder = true,
}) async* {
final int lastValidSubnet =
validateAndGetLastValidSubnet(subnet, firstHostId, lastHostId);
final List<Future<ActiveHost?>> activeHostsFuture = [];
final StreamController<ActiveHost> activeHostsController =
StreamController<ActiveHost>();
final List<Future<SendableActiveHost?>> activeHostsFuture = [];
final StreamController<SendableActiveHost> activeHostsController =
StreamController<SendableActiveHost>();

for (int i = firstHostId; i <= lastValidSubnet; i++) {
activeHostsFuture.add(
Expand All @@ -52,9 +72,9 @@ class HostScanner {
}

int i = 0;
for (final Future<ActiveHost?> host in activeHostsFuture) {
for (final Future<SendableActiveHost?> host in activeHostsFuture) {
i++;
final ActiveHost? tempHost = await host;
final SendableActiveHost? tempHost = await host;

progressCallback
?.call((i - firstHostId) * 100 / (lastValidSubnet - firstHostId));
Expand All @@ -65,11 +85,10 @@ class HostScanner {
yield tempHost;
}
}

static Future<ActiveHost?> _getHostFromPing({
static Future<SendableActiveHost?> _getHostFromPing({
required String host,
required int i,
required StreamController<ActiveHost> activeHostsController,
required StreamController<SendableActiveHost> activeHostsController,
int timeoutInSeconds = 1,
}) async {
await for (final PingData pingData
Expand All @@ -78,16 +97,16 @@ class HostScanner {
if (response != null) {
final Duration? time = response.time;
if (time != null) {
final ActiveHost tempActiveHost =
ActiveHost.buildWithAddress(address: host, pingData: pingData);
activeHostsController.add(tempActiveHost);
return tempActiveHost;
final tempSendableActivateHost = SendableActiveHost(host, pingData);
activeHostsController.add(tempSendableActivateHost);
return tempSendableActivateHost;
}
}
}
return null;
}


static int validateAndGetLastValidSubnet(
String subnet,
int firstHostId,
Expand All @@ -113,9 +132,7 @@ class HostScanner {
int timeoutInSeconds = 1,
ProgressCallback? progressCallback,
bool resultsInAddressAscendingOrder = true,
}) {
final StreamController<ActiveHost> activeHostsController =
StreamController<ActiveHost>();
}) async* {

const int scanRangeForIsolate = 51;
final int lastValidSubnet =
Expand All @@ -134,18 +151,19 @@ class HostScanner {
timeoutInSeconds.toString(),
resultsInAddressAscendingOrder.toString(),
]);

isolateManager.onMessage.listen((message) {
if (message is ActiveHost) {
await for (final message in isolateManager.onMessage.asBroadcastStream()){
if (message is SendableActiveHost) {
progressCallback
?.call((i - firstHostId) * 100 / (lastValidSubnet - firstHostId));
activeHostsController.add(message);

final activeHostFound = ActiveHost.fromSendableActiveHost(sendableActiveHost: message);
await activeHostFound.resolveInfo();
yield activeHostFound;
} else if (message is String && message == 'Done') {
isolateManager.stop();
}
});
}
}
return activeHostsController.stream;
}

/// Will search devices in the network inside new isolate
Expand All @@ -168,23 +186,17 @@ class HostScanner {

/// Will contain all the hosts that got discovered in the network, will
/// be use inorder to cancel on dispose of the page.
final Stream<ActiveHost> hostsDiscoveredInNetwork =
HostScanner.getAllPingableDevices(
final Stream<SendableActiveHost> hostsDiscoveredInNetwork =
HostScanner.getAllSendablePingableDevices(
subnetIsolate,
firstHostId: firstSubnetIsolate,
lastHostId: lastSubnetIsolate,
timeoutInSeconds: timeoutInSeconds,
resultsInAddressAscendingOrder: resultsInAddressAscendingOrder,
);

await for (final ActiveHost activeHostFound in hostsDiscoveredInNetwork) {
activeHostFound.deviceName.then((value) {
activeHostFound.mdnsInfo.then((value) {
activeHostFound.hostName.then((value) {
channel.sendResult(activeHostFound);
});
});
});
await for (final SendableActiveHost activeHostFound in hostsDiscoveredInNetwork) {
channel.sendResult(activeHostFound);
}
channel.sendResult('Done');
});
Expand Down
25 changes: 25 additions & 0 deletions network_tools/lib/src/models/active_host.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:dart_ping/dart_ping.dart';
import 'package:network_tools/src/models/mdns_info.dart';
import 'package:network_tools/src/models/open_port.dart';
import 'package:network_tools/src/models/sendable_active_host.dart';
import 'package:network_tools/src/network_tools_utils.dart';
import 'package:universal_io/io.dart';

Expand Down Expand Up @@ -65,6 +66,30 @@ class ActiveHost extends Comparable<ActiveHost> {
);
}

factory ActiveHost.fromSendableActiveHost({
required SendableActiveHost sendableActiveHost,
List<OpenPort> openPorts = const [],
MdnsInfo? mdnsInfo,
}) {
final InternetAddress? internetAddressTemp =
InternetAddress.tryParse(sendableActiveHost.address);
if (internetAddressTemp == null) {
throw 'Cant parse address ${sendableActiveHost.address} to InternetAddress';
}
return ActiveHost(
internetAddress: internetAddressTemp,
openPorts: openPorts,
pingData: sendableActiveHost.pingData,
mdnsInfoVar: mdnsInfo,
);
}

Future<void> resolveInfo() async {
await deviceName;
await mdnsInfo;
await hostName;
}

static const generic = 'Generic Device';
InternetAddress internetAddress;

Expand Down
7 changes: 7 additions & 0 deletions network_tools/lib/src/models/sendable_active_host.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:dart_ping/dart_ping.dart';

class SendableActiveHost{
SendableActiveHost(this.address, this.pingData);
final String address;
final PingData pingData;
}
2 changes: 1 addition & 1 deletion network_tools/lib/src/port_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class PortScanner {
443,
989,
990,
3389
3389,
];

/// Checks if the single [port] is open or not for the [target].
Expand Down
52 changes: 30 additions & 22 deletions network_tools_flutter/lib/src/host_scanner_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import 'dart:async';
import 'dart:isolate';
import 'dart:math';

import 'package:dart_ping/dart_ping.dart';
import 'package:dart_ping_ios/dart_ping_ios.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
import 'package:network_tools/network_tools.dart';
import 'package:universal_io/io.dart';

/// Scans for all hosts in a subnet.
class HostScannerFlutter {
Expand All @@ -12,17 +15,15 @@ class HostScannerFlutter {
/// It won't firstHostId again unless previous scan is completed due to heavy
/// resource consumption.
/// [resultsInAddressAscendingOrder] = false will return results faster but not in
static Future<Stream<ActiveHost>> getAllPingableDevices(
static Stream<ActiveHost> getAllPingableDevices(
String subnet, {
int firstHostId = HostScanner.defaultFirstHostId,
int lastHostId = HostScanner.defaultLastHostId,
int timeoutInSeconds = 1,
ProgressCallback? progressCallback,
bool resultsInAddressAscendingOrder = true,
}) async {
}) async* {
const int scanRangeForIsolate = 51;
final StreamController<ActiveHost> activeHostsController =
StreamController<ActiveHost>();
final int lastValidSubnet = HostScanner.validateAndGetLastValidSubnet(
subnet, firstHostId, lastHostId);

Expand All @@ -31,10 +32,18 @@ class HostScannerFlutter {
i += scanRangeForIsolate + 1) {
final limit = min(i + scanRangeForIsolate, lastValidSubnet);
final receivePort = ReceivePort();
final isolate = await Isolate.spawn(
HostScannerFlutter._startSearchingDevices, receivePort.sendPort);
dynamic isolate;

receivePort.listen((message) {
if(Platform.isAndroid || Platform.isIOS){
// Flutter isolate is not implemented for other platforms than these two
isolate = await FlutterIsolate.spawn(
HostScannerFlutter._startSearchingDevices, receivePort.sendPort);
} else {
isolate = await Isolate.spawn(
HostScannerFlutter._startSearchingDevices, receivePort.sendPort);
}

await for (final message in receivePort.asBroadcastStream()) {
if (message is SendPort) {
message.send([
subnet,
Expand All @@ -43,20 +52,23 @@ class HostScannerFlutter {
timeoutInSeconds.toString(),
resultsInAddressAscendingOrder.toString()
]);
} else if (message is ActiveHost) {
} else if (message is List<String>) {
progressCallback
?.call((i - firstHostId) * 100 / (lastValidSubnet - firstHostId));
activeHostsController.add(message);
final activeHostFound = ActiveHost.fromSendableActiveHost(
sendableActiveHost: SendableActiveHost(
message[0], PingData.fromJson(message[1])));
await activeHostFound.resolveInfo();
yield activeHostFound;
} else if (message is String && message == 'Done') {
isolate.kill();
}
});
}
}
return activeHostsController.stream;
}

/// Will search devices in the network inside new isolate
// @pragma('vm:entry-point')
@pragma('vm:entry-point')
static Future<void> _startSearchingDevices(SendPort sendPort) async {
DartPingIOS.register();
final port = ReceivePort();
Expand All @@ -71,23 +83,19 @@ class HostScannerFlutter {

/// Will contain all the hosts that got discovered in the network, will
/// be use inorder to cancel on dispose of the page.
final Stream<ActiveHost> hostsDiscoveredInNetwork =
HostScanner.getAllPingableDevices(
final Stream<SendableActiveHost> hostsDiscoveredInNetwork =
HostScanner.getAllSendablePingableDevices(
subnetIsolate,
firstHostId: firstSubnetIsolate,
lastHostId: lastSubnetIsolate,
timeoutInSeconds: timeoutInSeconds,
resultsInAddressAscendingOrder: resultsInAddressAscendingOrder,
);

await for (final ActiveHost activeHostFound in hostsDiscoveredInNetwork) {
activeHostFound.deviceName.then((value) {
activeHostFound.mdnsInfo.then((value) {
activeHostFound.hostName.then((value) {
sendPort.send(activeHostFound);
});
});
});
await for (final SendableActiveHost activeHostFound
in hostsDiscoveredInNetwork) {
sendPort
.send([activeHostFound.address, activeHostFound.pingData.toJson()]);
}
sendPort.send('Done');
}
Expand Down
3 changes: 3 additions & 0 deletions network_tools_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ dependencies:
sdk: flutter
network_tools:
path: ../network_tools
# Multi-platform network ping utility.
dart_ping: ^8.0.1
dart_ping_ios: ^3.0.0
universal_io: ^2.2.0
flutter_isolate: ^2.0.4

dev_dependencies:
flutter_test:
Expand Down
Loading

0 comments on commit af68c3a

Please sign in to comment.