samuel / oriontransfer.net wrote:
> I've been playing around with port scanners. Implemented in Go
> (goroutines), Python (asyncio) and Ruby (async).
>
> I wrote up the results here:
> https://github.com/socketry/async-await/tree/master/examples/port_scanner

Attached is the implementation for Threadlet/auto-fiber/wachamacallit
rebased against ruby trunk r63641:
   https://80x24.org/spew/20180613003524.9256-1-e / 80x24.org/raw

On a busy Linux VM, Threadlet was close to your Go implementation
in speed (timing results were unstable, however) and Ruby async
was around 3x slower behind (even with timing instabilities).

I kept on getting errors with the Python3 version
("Event loop is closed") so I never let it finish

I needed to deal with EPIPE because the system I tested on had RDS (16385)
enabled in the kernel which was triggering EPIPE (I don't know Go or Python):

```
diff --git a/examples/port_scanner/port_scanner.go b/examples/port_scanner/port_scanner.go
index 45f2d1c..ad0f049 100755
--- a/examples/port_scanner/port_scanner.go
+++ b/examples/port_scanner/port_scanner.go
@@ -55,7 +55,7 @@ func checkPortOpen(ip string, port int, timeout time.Duration) {
 		} else if strings.Contains(err.Error(), "refused") {
 			// fmt.Println(port, "closed", err.Error())
 		} else {
-			panic(err)
+			fmt.Println(port, "err", err.Error())
 		}
 		return
 	}
diff --git a/examples/port_scanner/port_scanner.py b/examples/port_scanner/port_scanner.py
index 372f0b3..ca9d41a 100755
--- a/examples/port_scanner/port_scanner.py
+++ b/examples/port_scanner/port_scanner.py
@@ -22,6 +22,8 @@ class PortScanner:
                 # print("{} closed".format(port))
             except asyncio.TimeoutError:
                 print("{} timeout".format(port))
+            except SystemError:
+                print("{} error".format(port))
 
     def start(self, timeout=1.0):
         self.loop.run_until_complete(asyncio.gather(
diff --git a/examples/port_scanner/port_scanner.rb b/examples/port_scanner/port_scanner.rb
index 0e4160e..3ac0109 100755
--- a/examples/port_scanner/port_scanner.rb
+++ b/examples/port_scanner/port_scanner.rb
@@ -25,6 +25,8 @@ class PortScanner
     # puts "#{port} closed"
   rescue Async::TimeoutError
     puts "#{port} timeout"
+  rescue SystemCallError => e
+    puts "#{port} #{e.message}"
   end
 
   async def start(timeout = 1.0)
```
#!/usr/bin/env ruby=0Arequire 'socket'=0A=0Aclass PortScanner=0A  def initi=
alize(host: '0.0.0.0', ports:, batch_size: 1024)=0A    @host      =3D host=
=0A    @ports     =3D ports.to_a=0A    @batch_size =3D batch_size=0A  end=
=0A=0A  def scan_port(port, timeout)=0A    peer =3D Socket.tcp(@host, port,=
 connect_timeout: timeout)=0A    puts "#{port} #{peer.wait_writable(timeout=
) ? 'open' : 'timeout'}"=0A    peer.close=0A  rescue Errno::ECONNREFUSED=0A=
    # puts "#{port} closed"=0A  rescue SystemCallError =3D> e=0A    puts "#=
{port} #{e.message}"=0A  end=0A=0A  def start(timeout =3D 1.0)=0A    @batch=
_size.times.map do=0A      Threadlet.start do=0A         while port =3D @po=
rts.shift=0A           scan_port(port, timeout)=0A         end=0A      end=
=0A    end.each(&:join)=0A  end=0Aend=0A=0Alimits =3D Process.getrlimit(Pro=
cess::RLIMIT_NOFILE)=0Abatch_size =3D [512, limits.first].min=0Ahost =3D "1=
27.0.0.1"=0Ascanner =3D PortScanner.new(host: host, ports: Range.new(1, 655=
35), batch_size: batch_size)=0A=0Ascanner.start=0A

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>