The Ruby library EventMachine makes us easy to write a network server/client software on Ruby.
Here's an echo server described in the EventMachine README.
require 'eventmachine'
module EchoServer
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data data
send_data ">>>you sent: #{data}"
close_connection if data =~ /quit/i
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 8081, EchoServer
}
Let's try it. Save the code to a.rb
and do the command:
$ rake build
$ ruby -Ilib a.rb
Now the echo server started on port 8081. Let me check it on another terminal.
$ telnet localhost 8081
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying fe80::1...
telnet: connect to address fe80::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hi
>>>you sent: hi
hey
>>>you sent: hey
It certainly worked.
Say server
OSX has say
command which speaks the given sentence. Now I'll
write Say Server to handle notification from a remote machine.
require 'eventmachine'
module SayServer
def receive_data(data)
system 'say', data
close_connection
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 8081, SayServer
}
It was too easy to fix it.
Unfortunately I didn't know how to make the server say a word in one line. The following command didn't work like I expected.
$ echo 'hi' | telnet localhost 8081
Spawned Processes
I found an interesting document in docs/SPAWNED_PROCESSES
.
Spawned Processes in EventMachine are inspired directly by the "processes" found in the Erlang programming language. EM deliberately borrows much (but not all) of Erlang's terminology. However, EM's spawned processes differ from Erlang's in ways that reflect not only Ruby style, but also the fact that Ruby is not a functional language like Erlang.
Note that the word spawn
doesn't mean Kernel.#spawn
in ruby
1.9. This EventMachine.spawn
generates something like an Erlang's
tiny process, which can receive and send a message each other.
Here's sample ping-pong code:
require 'eventmachine'
EM.run {
pong = EM.spawn {|x, ping|
sleep 0.1
puts "Pong received #{x}"
ping.notify( x-1 )
}
ping = EM.spawn {|x|
if x > 0
puts "Pinging #{x}"
pong.notify x, self
else
EM.stop
end
}
ping.notify 3
}
p :finished
The result is:
$ ruby -Ilib a.rb
Pinging 3
Pong received 3
Pinging 2
Pong received 2
Pinging 1
Pong received 1
:finished
I expected that the message :finished
would appers during the
ping-pong, but didn't. I fixed the line 'ping.notify 3
' into
'Thread.start { ping.notify 3}
', but the result was still same.
Even worse, 'fork { ping.notify 3 }
' got frozen up.
[Aug 18 Added]
Stoyan suggested that EM::Deferrable
is suit to be used instead of Thread or spawn.
The page he introduced shows the following sample code (I added comments on it a little to show the order of processing)
class Worker
include EM::Deferrable
def heavy_lifting
# (3)
3.times do |i|
puts "Lifted #{i}"
sleep 0.1
end
set_deferred_status :succeeded
end
end
EM.run do
# (1)
worker = Worker.new
worker.callback {
# (4)
p "done!"
}
Thread.new {
# (2)
worker.heavy_lifting
# (5)
EM.stop
}
# (3)'
puts "resuming remaining program operations"
end
And the result is
Lifted 0
resuming remaining program operations
Lifted 1
Lifted 2
"done!"
For parallel jobs better try EM::Deferrable, then Thread or spawn. EventMachine is a single threaded, events based. For example: http://nutrun.com/weblog/distributed-programming-with-jabber-and-eventmachine/
ReplyDeleteThanks! I added a paragraph about it on this post.
ReplyDelete