I'm somewhat new to Ruby and have been playing around with some very simple
socket code (starting the script and then telnetting to the box on the port
specified). I wanted to allow it to accept more than one request at a time
so I added some threads. However, now that I've added some thread code, I
can't get the script to end properly (I guess "as expected" is more
appropriate). Here's some code:
Ok, before I talk about your code, you may like to know that you don't actually need to use threads to handle multiple sockets at once. There is a method in the kernel called "select" that can be used thus:
require 'socket'
server = TCPServer.new(5000)
sockets =
while( server )
# Wait for read events on the server or sockets.
socketsWithEvents = select( [server] + sockets )[0]
# Process each socket that has an event.
socketsWithEvents.each do |soc|
if soc == server
sockets << soc.accept
else
# Close the socket if the other end has closed.
if soc.eof?
soc.close
sockets.delete(soc)
break
end
s = soc.gets.chomp
soc.puts "You said \"#{s}\", you land lubber!"
server=nil if s=="close"
end
end
end
You should be able to begin as many concurrent telnet sessions to port 5000 as you wish, and anything typed on any will be pirated back at.
... Perhaps other more knowledgeable Ruby users could comment on the advantages or disadvantages of using threads or avoiding them.
As a top tip, I'd heartily recommend (if you don't already) playing about with "irb", and trying out sockets "hands on". It's brilliant to be able to write TCPServer.new(5000).accept, and wait for it to return a new connection
require 'socket'
p = 3333
threads =
s = ''server = TCPServer.new('0.0.0.0', p)
while (session = server.accept)
threads << Thread.new(session) do |ts|
ts.print "What's your name?\r\n"
puts "Request: #{s = ts.gets.chomp!}"
ts.print "\r\nHello #{s}, thanks for stopping by\r\n\r\n"
ts.close
end
break if s == 'close'
end
I've just read someone else's reply mentioning that you have a "race condition". What's happening is that when you create the thread, you've got two lines of execution that are "racing" along through your code, and you can never really know which one is going to be in the lead ay any moment...
Thread.new builds a new thread that runs the block you passed in. This thread is handling the newly created socket.
The original thread instantly returns from the Thread.new, and starts processing just after the block on the line "break if s == 'close'".
If you're not familiar with threads, it's almost as if you've suddenly got two processors instead of one - both of them are dashing through your code executing statements happily, which is why careless thread programming can cause merry havoc
Generally, you can't make guarantees about the speed that the two threads will progress through the code they're assigned to. One may go slowly for a while, and then very fast. If you need one thread to hang back (the main thread, in this case) while the other does something (the socket's thread does the read and sets the variable "s"), then you _must_ use synchronisation mechanisms (Look up "monitors" for starters).
In your case, the original thread will (usually) evaluate the "break if..." line almost instantly, long before the new thread even gets a chance to write to the thread, let alone read from it in to "s". When the original thread checks "s", it wont have been updated to by the newly spawned thread, so you wont execute the conditional break (until the next time through the loop).
HTH, cheers (and have fun
Benjohn