I’m getting started with async IO and trying to use it in a project. This project works in the following manner:
There’s a file hosts.txt which contains one IPv4 address on each line and looks like:
1.2.3.4
5.6.7.8
9.10.11.12
There’s a method scanAll, which reads the hosts.txt file and calls scanHost, by passing the IP and a port (port is the same for all IPs).
Without async, the code looks like:
const PortStatus = enum {
open,
closed,
unknown,
};
fn scan(io: std.Io, ipv4: []const u8, port: u16) !PortStatus {
const address = try std.Io.net.IpAddress.parseIp4(ipv4, port);
const stream = std.Io.net.IpAddress.connect(
address,
io,
.{ .mode = .stream, .protocol = .tcp },
) catch |err| {
return switch (err) {
error.ConnectionRefused => .closed,
else => .unknown,
};
};
defer stream.close(io);
return .open;
}
// scanning one host on one port is a supported functionality; this is not merely a helper function
pub fn scanHost(io: std.Io, ipv4: []const u8, port: u16) !void {
const port_status = try scan(io, ipv4, port);
if (port_status == .open) {
std.debug.print("Port {d} is open on host {s}\n", .{ port, ipv4 });
} else {
std.debug.print("Port {d} is not open on host {s}\n", .{ port, ipv4 });
}
}
pub fn scanAll(io: std.Io, port: u16) !void {
const input = try std.Io.Dir.cwd().openFile(io, "./hosts.txt", .{ .mode = .read_only });
defer input.close(io);
var buffer: [512]u8 = undefined;
var reader = input.reader(io, &buffer);
const file = &reader.interface;
while (try file.takeDelimiter('\n')) |line| {
try scanHost(io, line, port);
}
}
and outputs the below (the sequence is the same as in the hosts.txt file):
Port 80 is open on host 1.2.3.4
Port 80 is open on host 5.6.7.8
Port 80 is open on host 9.10.11.12
If the above output could be in any order, how can I use concurrency (and / or parallelism) ?