-
-
Save picatz/e8a6f526ce236481837f3cdbb9b84152 to your computer and use it in GitHub Desktop.
| require 'async/io' | |
| require 'async/await' | |
| require 'async/semaphore' | |
| class PortScanner | |
| include Async::Await | |
| include Async::IO | |
| def initialize(host: '127.0.0.1', ports:) | |
| @host = host | |
| @ports = ports | |
| @semaphore = Async::Semaphore.new(`ulimit -n`.to_i) | |
| end | |
| def scan_port(port, timeout: 0.5) | |
| timeout(timeout) do | |
| Async::IO::Endpoint.tcp(@host, port).connect do |peer| | |
| peer.close | |
| puts "#{port} open" | |
| end | |
| end | |
| rescue Errno::ECONNREFUSED, Async::TimeoutError | |
| puts "#{port} closed" | |
| rescue Errno::EMFILE | |
| sleep timeout | |
| retry | |
| end | |
| async def start(timeout: 0.5) | |
| @ports.map do |port| | |
| @semaphore.async do | |
| scan_port(port, timeout: timeout) | |
| end | |
| end.collect(&:result) | |
| end | |
| end | |
| scanner = PortScanner.new(ports: (1..1024)) | |
| scanner.start |
The syncing part is pretty cool! Not totally needed for my cases, but a really nice touch. ๐
@picatz Because of the way async works - scanner.start will wait until all tasks complete anyway.
If you add
require 'async/semaphore'
def initialize
@semaphore = Async::Semaphore.new(`ulimit -n`.to_i)
...
end
# remove async from def scan_port
async def start(timeout: 0.5)
@ports.map{|port|
@semaphore.async do
scan_port(port, timeout: timeout)}
end
end.collect(&:result)
end
You should avoid the need for handling Errno::EMFILE.
You need to use async v1.9.1 for this to work.
@ioquatix Async::Semaphore seems to be a nice abstraction to help clean up the code and provide a similar semantic for people that understand Ruby's multi-threading APIs. ๐
However, I still seem to reach the too many files limit since, I'd assume, other operations on my system have files that are open. So, I still end up hitting Errno::EMFILE, or using it to keep track of the situation myself with raise Errno::EMFILE while @open_fds >= @fd_limit which I don't mind doing in this case -- I think.
To add synchronisation to the port scanner, try adding
Then, instead of printing the result you can return it.