How to have an eventmachine server just write data? - ruby

I need to implement a server which only writes data, doesn't receive it. All of the eventmachine server examples I've found always have the server receive data first, and then respond with data. I need it to just start writing data to a client after a client connects.
I tried just putting a loop in post_init, but that doesn't seem to work... the client connects, the server writes, but the client never seems to receive anything. Suggestions?
The test server:
require 'rubygems'
require 'eventmachine'
require 'time'
module TestServer
def post_init
puts "-- client connected, sending data --"
while true do
send_data "Hello from TestServer\n"
puts "sent #{Time.now.iso8601}"
end
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 4001, TestServer
puts 'running test server on 4001'
}
The test client:
require 'rubygems'
require 'eventmachine'
module Forwarder
def post_init
puts "-- connected to server --"
end
def receive_data data
# none of the following is ever output
puts "in receive_data"
puts data
end
end
EventMachine::run {
EventMachine::connect '127.0.0.1', 4001, Forwarder
}
Thanks...

Thanks to tmm1 on #eventmachine, got this figured out. Client is the same. Server code is:
require 'rubygems'
require 'eventmachine'
require 'time'
module TestServer
def post_init
puts "-- client connected --"
#timer = EM::PeriodicTimer.new(0.1) {
send_data "Hello from TestServer at #{Time.now.iso8601}\n"
}
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 4001, TestServer
puts 'running test server on 4001'
}

Related

EventMachine not receiving TCP data on localhost

Using eventmachine gem i am trying to send and receive data on localhost. Following is the code of my client and server files.
server.rb
class BCCServer < EM::Connection
attr_accessor :server_socket
def post_init
puts "BCC Server"
end
def recieve_data(data)
puts "Received data: #{data}"
send_data "You sent: #{data}"
end
end
EM.run do
EM.start_server("0.0.0.0", 3000, BCCServer)
end
client.rb
class DCClient < EventMachine::Connection
def post_init
puts "Sending "
send_data "send data"
close_connection_after_writing
end
def receive_data(data)
puts "Received #{data.length} bytes"
end
def unbind
puts 'Connection Lost !'
end
end
EventMachine.run do
EventMachine::connect("127.0.0.1", 3000, DCClient)
end
I executed both server and client files in separate console. Following is the output of client
Client output
Sending
Connection Lost !
Server output
BCC Server
............>>>10
In the server file i have printed the data received but its showing "............>>>10". Where i am doing the mistake?
Thanks
if you look at the EM::Connection implementation
https://github.com/eventmachine/eventmachine/blob/master/lib/em/connection.rb
def receive_data data
puts "............>>>#{data.length}"
end
Method receive_data returns exactly what you are experiencing.
That means original method gets called and not yours. That means one thing. You have a typo in a method which you tried to override :)
In BCCServer you have
recieve_data(data)
instead of
receive_data(data)

Ctrl+C not killing Sinatra + EM::WebSocket servers

I'm building a Ruby app that runs both an EM::WebSocket server as well as a Sinatra server. Individually, I believe both of these are equipped to handle a SIGINT. However, when running both in the same app, the app continues when I press Ctrl+C. My assumption is that one of them is capturing the SIGINT, preventing the other from capturing it as well. I'm not sure how to go about fixing it, though.
Here's the code in a nutshell:
require 'thin'
require 'sinatra/base'
require 'em-websocket'
EventMachine.run do
class Web::Server < Sinatra::Base
get('/') { erb :index }
run!(port: 3000)
end
EM::WebSocket.start(port: 3001) do |ws|
# connect/disconnect handlers
end
end
I had the same issue. The key for me seemed to be to start Thin in the reactor loop with signals: false:
Thin::Server.start(
App, '0.0.0.0', 3000,
signals: false
)
This is complete code for a simple chat server:
require 'thin'
require 'sinatra/base'
require 'em-websocket'
class App < Sinatra::Base
# threaded - False: Will take requests on the reactor thread
# True: Will queue request for background thread
configure do
set :threaded, false
end
get '/' do
erb :index
end
end
EventMachine.run do
# hit Control + C to stop
Signal.trap("INT") {
puts "Shutting down"
EventMachine.stop
}
Signal.trap("TERM") {
puts "Shutting down"
EventMachine.stop
}
#clients = []
EM::WebSocket.start(:host => '0.0.0.0', :port => '3001') do |ws|
ws.onopen do |handshake|
#clients << ws
ws.send "Connected to #{handshake.path}."
end
ws.onclose do
ws.send "Closed."
#clients.delete ws
end
ws.onmessage do |msg|
puts "Received message: #{msg}"
#clients.each do |socket|
socket.send msg
end
end
end
Thin::Server.start(
App, '0.0.0.0', 3000,
signals: false
)
end
I downgrade thin to version 1.5.1 and it just works. Wired.

Ruby EventMachine testing

My first question concerning Ruby.
I'm trying to test EventMachine interaction inside the Reactor loop - I guess it could be classified as "functional" testing.
Say I have two classes - a server and a client. And I want to test both sides - I need to be sure about their interaction.
Server:
require 'singleton'
class EchoServer < EM::Connection
include EM::Protocols::LineProtocol
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
Client:
class EchoClient < EM::Connection
include EM::Protocols::LineProtocol
def post_init
send_data "Hello"
end
def receive_data(data)
#message = data
p data
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
So, I've tried different approaches and came up with nothing.
The fundamental question is - could I somehow test my code with RSpec, using should_recive?
EventMachine parameter should be a class or a module, so I can't send instantiated/mocked code inside. Right?
Something like this?
describe 'simple rspec test' do
it 'should pass the test' do
EventMachine.run {
EventMachine::start_server "127.0.0.1", 8081, EchoServer
puts 'running echo server on 8081'
EchoServer.should_receive(:receive_data)
EventMachine.connect '127.0.0.1', 8081, EchoClient
EventMachine.add_timer 1 do
puts 'Second passed. Stop loop.'
EventMachine.stop_event_loop
end
}
end
end
And, if not, how would you do it with EM::SpecHelper? I have this code using it, and can't figure out what I'm doing wrong.
describe 'when server is run and client sends data' do
include EM::SpecHelper
default_timeout 2
def start_server
EM.start_server('0.0.0.0', 12345) { |ws|
yield ws if block_given?
}
end
def start_client
client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
yield client if block_given?
return client
end
describe "examples from the spec" do
it "should accept a single-frame text message" do
em {
start_server
start_client { |client|
client.onopen {
client.send_data("\x04\x05Hello")
}
}
}
end
end
end
Tried a lot of variations of these tests and I just can't figure it out. I'm sure I'm missing something here...
Thanks for your help.
The simplest solution that I can think of is to change this:
EchoServer.should_receive(:receive_data)
To this:
EchoServer.any_instance.should_receive(:receive_data)
Since EM is expecting a class to start a server, the above any_instance trick will expect any instance of that class to receive that method.
The EMSpecHelper example (while being official/standard) is quite convoluted, I'd rather stick with the first rspec and use any_instance, just for simplicity's sake.

The tcp client can not receive data from eventmachine

there is the code, the client:
require 'rubygems'
require 'benchmark'
require 'socket'
i=0
TCPSocket.open "127.0.0.1", 8080 do |s|
s.send "#{i}th sending", 0
if line = s.gets
puts line
end
end
the server:
require 'rubygems'
require 'benchmark'
require 'eventmachine'
class Handler < EventMachine::Connection
def receive_data(data)
sleep 2 # simulate a long running request
send_data "send_response"
puts data
end
end
EventMachine::run {
EventMachine::start_server("0.0.0.0", 8080, Handler)
puts "Listening..."
}
The client can not print anything
It's an interaction between s.gets in the client and send_data "send_response" in the server.
Your small test works fine for me when I change:
send_data "send_response"
to
send_data "send_response\n"
The s.gets is waiting for a newline from the remote client. None comes.

Multiple servers in a single EventMachine reactor

Is it possible to run multiple servers within a single event machine?
What I mean is that multiple services can be used concurrently by a single client connection. For example, a login server authenticates a user and then a user can use both a chat room and a simple game such as checkers with a single client socket concurrently?
Or do I need multiple eventmachine reactors for each service?
I tried this and it's working:
#!/usr/bin/env ruby
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
EventMachine::start_server "127.0.0.1", 8082, EchoServer
EventMachine::start_server "127.0.0.1", 8083, EchoServer
}
Here you get 3 echo services with different ports. (I was too lazy to implement different services.)
So, it's very easy to build a huge multi service wrapper.
Update
Simple code example for condition based start of a EM server:
#!/usr/bin/env ruby
# encoding: utf-8
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
$check_ok = false
EventMachine::run {
puts "checker var is: #{$check_ok}"
EventMachine::start_server "127.0.0.1", 8081, EchoServer
EventMachine::start_server "127.0.0.1", 8082, EchoServer
puts "echos on 8081 and 8082 started."
# periodic timer check - every 1 sec
EventMachine.add_periodic_timer(1) {
if $check_ok
EventMachine::start_server "127.0.0.1", 8083, EchoServer
$check_ok = false
puts "echo on 8083 started!"
end
}
# timer triggered after 10 secs - only once!
EventMachine.add_timer(10) {
$check_ok = true
puts "checker var is #{$check_ok} now!"
}
}
In this example the echo server on port 8083 is started ~10 secs after app start. Try to telnet localhost 8083 before and after this timer, you'll see the effect.
You also can use values lower than 1 sec like 0.01 for every 1/100th sec checks.
This might be your starting point for your own ideas. The periodic timer is your internal loop, where you hook in your conditional checks for starting further services.
Good tutorial (PDF): eventmachine introduction (blog post)

Resources