The tcp client can not receive data from eventmachine - ruby

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.

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")

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.

Can I send an object via TCP using Ruby?

I'm trying to send and "message" object via TCP on Ruby and my client class simply don't see any thing comming. What am I doing wrong?
My message class (that I'm trying to send)
class Message
attr_reader :host_type, :command, :params
attr_accessor :host_type, :command, :params
def initialize(host_type, command, params)
#host_type = host_type
#command = command
#params = params
end
end
My "Server" class
require 'socket'
require_relative 'message'
class TCP_connection
def start_listening
puts "listening"
socket = TCPServer.open(2000)
loop {
Thread.start(socket.accept) do |message|
puts message.command
end
}
end
def send_message
hostname = 'localhost'
port = 2000
s = TCPSocket.open(hostname, port)
message = Message.new("PARAM A", "PARAM B", "PARAM C")
s.print(message)
s.close
end
end
Below an example of client server comunication via json. You will expect something like this RuntimeError: {"method1"=>"param1"}. Instead of raising errors, process this json with the server logic.
Server
require 'socket'
require 'json'
server = TCPServer.open(2000)
loop {
client = server.accept
params = JSON.parse(client.gets)
raise params.inspect
}
Client
require 'socket'
require 'json'
host = 'localhost'
port = 2000
s = TCPSocket.open(host, port)
request = { 'method1' => 'param1' }.to_json
s.print(request)
s.close
You need to serialize your object. The most portable way to do this is using YAML. First, require the yaml library:
require "yaml"
Than, replace s.print(message) with s.print(YAML.dump(message)).

How to have an eventmachine server just write data?

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'
}

Ruby Tcp Server class with non-blocking or multithread functionality

Can't find any gem or class which can help to made a non-blocking/multithread server.
Where to find any?
The Ruby docs on sockets have some pretty good examples. Using information from that page, I cobbled together a simple client and server using non-blocking sockets. These are mostly copies of code from that page with a few changes.
The simple server code (with the accept_nonblock call that you may be interested in):
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(6212, 'localhost')
socket.bind(sockaddr)
socket.listen(5)
begin
client_socket, client_sockaddr = socket.accept_nonblock
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR, Errno::EWOULDBLOCK
IO.select([socket])
retry
end
puts client_socket.readline.chomp
client_socket.puts "hi from the server"
client_socket.close
socket.close
And a client that talks to it:
require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(6212, 'localhost')
begin
socket.connect_nonblock(sockaddr)
rescue Errno::EINPROGRESS
IO.select(nil, [socket])
begin
socket.connect_nonblock(sockaddr)
rescue Errno::EINVAL
retry
rescue Errno::EISCONN
end
end
socket.write("hi from the client\n")
results = socket.read
puts results
socket.close
Take a look at EventMachine. Here’s a quick example:
require "rubygems"
require "eventmachine"
module EchoServer
def receive_data (data)
send_data "You said: #{data}"
end
end
EventMachine::run do
EventMachine::start_server "0.0.0.0", 5000, EchoServer
end
Use Celluloid::IO
This is the primary purpose of Celluloid::IO and it is extremely good at what it does:
https://github.com/celluloid/celluloid-io
A few example servers...
https://github.com/celluloid/celluloid-dns
https://github.com/celluloid/reel

Resources