I was recently asked if I had the content of some articles that I posted a long time ago on a blog I used to run. After some searching I managed to scrape together the content using the Wayback Machine. It's faithfully recreated here without changes, something I should have done when I first bought the barkingiguana.com domain.
Continuing my [previous adventure](/2011/03/01/socket-programming-in-ruby/) in socket programming with Ruby, today I've attempted to communicate with multiple sockets concurrently.
The idea is simple: spin up a thread for each port we want to check, and let them all run at once.
```
kawaii:~ craig$ irb
irb(main):001:0> require 'socket'
=> true
irb(main):002:0> threads = []
=> []
irb(main):003:0> ports = [22,23,24,25,26,27,28,29,30].freeze
=> [22, 23, 24, 25, 26, 27, 28, 29, 30]
irb(main):004:0> for port in ports
irb(main):005:1> threads << Thread.new(port) { |p|
irb(main):006:2* puts "Checking if port " + p.to_s + " is open..."
irb(main):007:2> begin
irb(main):008:3* t = TCPSocket.new('xeriom.net', p)
irb(main):009:3> t.close
irb(main):010:3> puts "Port " + p.to_s + " is open."
irb(main):011:3> rescue
irb(main):012:3> puts "Port " + p.to_s + " is not open."
irb(main):013:3> end
irb(main):014:2> }
irb(main):015:1> end
Checking if port 22 is open...Checking if port 23 is open...Checking if port 24 is open...
Checking if port 25 is open...
Checking if port 26 is open...
Checking if port 27 is open...
Port 22 is open.Checking if port 28 is open...
Checking if port 29 is open...
Checking if port 30 is open...
=> [22, 23, 24, 25, 26, 27, 28, 29, 30]
irb(main):016:0>
Port 24 is not open.Port 23 is not open.Port 29 is not open.Port 28 is not open.Port 25 is open.
Port 27 is not open.Port 26 is not open.Port 30 is not open.
```
The output is a jumbled mess because threads are writing to STDOUT whenever they feel like it -- but that's not the point. We can see that ports 22 (SSH) and 25 (SMTP) are open, and everything else is closed. I've just built a simple port scanner in 15 lines of Ruby. It's not pretty, but it works.
The [Ruby thread tutorial](http://www.rubycentral.com/book/tut_threads.html) at [Ruby Central](http://www.rubycentral.com/) mentions a fairly important caveat: if a thread executes something at the OS level that takes a long time to return, it can freeze the entire interpreter. That sounds bad.
Interestingly though, it doesn't seem to apply to TCPSocket operations. Adding in a few checks (left as an exercise for the reader), it seems that the only thing limiting the number of active threads is the overhead of creating them. There are up to 10 running at once with the above code, and I suspect you could push that number considerably higher if thread creation were faster.
Tomorrow (or maybe later today) I'll be attempting to use [ActiveRecord](http://rubyonrails.org/api/classes/ActiveRecord/Base.html) outside of [Rails](http://rubyonrails.org/). I know it can be done -- I just don't know how hard it is yet.