TCP server using Ruby socket works but not using eventmachine - ruby

I wrote a TCP server using below code. This is to receive GPS location data via GSM network from a remote GPS sensor.
require 'socket'
server = TCPServer.open(2000) # Listen on port 2000
sockets = [server] # An array of sockets we'll monitor
log = STDOUT # Send log messages to standard out
while true
ready = select(sockets)
readable = ready[0]
readable.each do |socket|
if socket == server
client = server.accept
sockets << client
log.puts "Accepted connection from #{client.peeraddr[2]}"
while msg = client.gets
puts msg
end
else
input = socket.gets
if !input
log.puts "Client on #{socket.peeraddr[2]} disconnected"
sockets.delete(socket)
socket.close
next
end
input.chop!
if (input == "quit")
socket.puts("Bye");
log.puts "Closing connnection to #{socket.peeraddr[2]}"
sockets.delete(socket)
socket.close
else
socket.puts(input.reverse)
end
end
end
end
and then I wrote one using Eventmachine. Code as below:
require 'eventmachine'
module EchoServer
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data data
puts data
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 2000, EchoServer
}
However, this eventmachine code will not receive nor display the data. Any part of the Eventmachine code that is wrong?
Thanks

I think your problem is that you are listening on localhost only, try this:
EM::run do
EM.start_server "0.0.0.0", 2000, EchoServer
end

Related

Ruby 2.3 TCP Server Daemon for Postfix SMTP Access Policy Delegation

I'm get stuck to write a tcp server daemon in Ruby 2.3. The issue is, that my connection is not going further, when postfix is communicating with the ruby tcp server. If i do connect to the ruby tcp server by telnet, everything works fine. My code is as follows:
require 'socket'
require_relative 'postfix_delegation_object'
class Server
attr_reader :binding, :port
def initialize(binding: '127.0.0.1', port: '1988')
puts "Starting server now!"
puts "Listening on tcp://#{binding}:#{port}"
socket = TCPServer.new(binding, port)
while client = socket.accept
Thread.new { handle_connection(client) }
end
end
def handle_connection(client)
hash_values = {}
puts "New client! #{client}"
while line = client.gets
if line.include? "="
key, val = line.split('=')
hash_values[key] = val.to_s.strip
else
pdo = PostfixDelegationObject.new(hash_values)
client.write("action=dunno")
end
end
end
end
I could solve it by my own. I just had to enter twice '\n'
like this:
client.write("action=dunno\n\n")
This one would not work:
client.write("action=dunno")
client.write("\n\n")

Ruby -- Why my server is stop working

I wrote my app that use Lua and connect to my server using Ruby but when the application start, it seem stop working. I'm not sure what the problem is because I'm new to Ruby but I ever used Python and have the same problem Corona Simulator stop working after connecting to server
I'm curious is Ruby have something like http protocol requirement or not? Below is my code using puts to send data to client.
require "socket"
class Server
def initialize(ip,port)
puts "Server Start!!"
#server = TCPServer.open(ip,port)
#connections = Hash.new
#rooms = Hash.new
#clients = Hash.new
#connections[:server] = #server
#connections[:rooms] = #rooms
#connections[:clients] = #clients
end
def run
loop {
Thread.start(#server.accept) do |client|
puts "New client connected."
nickname = client.gets.chomp.to_sym
puts "#{nickname} #{client}"
#connections[:clients][nickname] = client
client.puts "Hi there"
client.print "\n"
client.close
end
}.join
end
end
server = Server.new("127.0.0.1",8080)
server.run
add
client.print "\n
client.close
at the end of thread function and it should work

How to run a background thread in ruby?

I am new to ruby and thought it would be a great idea to rebuild a simple chat program I made in C#.
I am using Ruby 2.0.0 MRI (Matz’s Ruby Implementation).
The problem is I want to have I/O for simple server commands while the server is running.
This is the server that was taken from the sample. I added the commands method that uses gets() to get input. I want this method to run as a thread in the background, but the thread is blocking the other thread.
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
def commands
x = 1
while x == 1
exitProgram = gets.chomp
if exitProgram == "exit" || exitProgram == "Exit"
x = 2
abort("Exiting the program.")
end
end
end
def main
Thread.start(commands)
Thread.start(server.accept)
loop { # Servers run forever
Thread.start(server.accept) do |client|
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
end
}
end
main
This is the client so far.
require 'socket' # Sockets are in standard library
hostname = 'localhost'
port = 2000
s = TCPSocket.open(hostname, port)
while line = s.gets # Read lines from the socket
puts line.chop # And print with platform line terminator
end
s.close # Close the socket when done
gets.chomp
Read the documentation for Thread.new (which is the same as Thread.start here)
Thread.start(commands) runs the commands method and passes its return value to a thread (which then does nothing). It's blocking because you aren't starting any threads when gets is called. You want
Thread.start { commands }
Here's a similar demo script that works just like you would expect
def commands
while gets.strip !~ /^exit$/i
puts "Invalid command"
end
abort "Exiting the program"
end
Thread.start { commands }
loop do
puts "Type exit:"
sleep 2
end

EventMachine send data to one client (From Multi Server )

I have developed multiple eventmachine servers which are like
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
}
Now I need to send data to the client as per the port only 8082. If I have all the connections open . Server needs to send back data to the perticular server.
So If From 8081 I get the request I need to send it to 8082 client.
How do I send that?
According to the modification of the original question, I posted a new answer.
You will need to track the server port for each connection. And when a new connection is established from port 8082, store that connection until it is closed. And when you get data from clients connected by 8081 port, send data to all connections stored before.
require 'eventmachine'
$clients = []
module EchoServer
def initialize(port)
#port = port
end
def post_init
puts "-- someone connected to the echo server!"
$clients << self if #port == 8082
end
def receive_data data
send_data ">>>you sent: #{data}"
# data is from a client connected by 8081 port
if #port == 8081
$clients.each do |c|
c.send_data ">>>server send: #{data}"
end
end
close_connection if data =~ /quit/i
end
def unbind
puts "-- someone disconnected from the echo server!"
$clients.delete self if #port == 8082
end
end
# Note that this will block current thread.
EventMachine.run {
# arguments after handler will be passed to initialize method
EventMachine.start_server "127.0.0.1", 8081, EchoServer, 8081
EventMachine.start_server "127.0.0.1", 8082, EchoServer, 8082
EventMachine.start_server "127.0.0.1", 8083, EchoServer, 8083
}
Run telnet 127.0.0.1 8082 under you console/shell.
-> ~ $ telnet 127.0.0.1 8082
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
hello
>>>you sent: hello
quit
Connection closed by foreign host.
If you want to send data from Ruby code, take a look at socket library.
require 'socket'
s = TCPSocket.new '127.0.0.1', 8082
s.puts "Hello"
puts s.gets #=> >>>you sent: Hello
s.close

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