How can I only read one line of data from a TCPSocket in Ruby? - ruby

I'm using the following code to connect to a network service i'm writing (thats backed by EventMachine) and I'm having a bit of trouble getting into a situation allowing me to use one socket connection to execute multiple commands.
#!/usr/bin/env ruby
require 'socket'
opts = {
:address => "0.0.0.0",
:port => 2478
}
connection = TCPSocket.open opts[:address], opts[:port]
# Get ID
connection.print "ID something"
puts connection.read
# Status
connection.print "STATUS"
puts connection.read
# Close the connection
connection.close
Here's what my EventMachine server hander looks like...
module ConnectionHandler
def receive_data data
send_data "Some output #{data}"
end
end
However, my first ruby script hangs when it executes connection.read as I presume its waiting for the connection to close so it knows its got all of the data? This is not what I want to happen.
My socket server will just take one command (on one line) and return one line of output.
Any ideas how I can do this? Thanks.

It turns out the connection.gets method will return a line of data received if the server sends a response ending in a \n character. So I just added \n to the end of my send_data call and switch to using puts connection.gets and it worked great!

Related

Read data both ways TCPServer Ruby

im new in Ruby and Im trying to set up a TCPServer and a Client, but Im having trouble getting the data from the client to the server because for some reason when the client connects, the connection is freezed inside the while loop.
Here is the code:
server.rb
require "socket"
server = TCPServer.new 1234
test = ""
loop do
session = server.accept
puts "Entering enter code herewhile loop."
while line = session.gets
puts "Inside while loop"
test << line
end
puts "Finished reading data"
puts "Data recieved - #{test}" # Read data from client
session.write "Time is #{Time.now}" # Send data to clent
session.close
end
client.rb
require "socket"
socket = TCPSocket.open("localhost", 1234)
socket.puts "Sending data.." # Send data to server
while(line = socket.gets)
puts line
end # Print sever response
socket.close
The server prints "Inside while loop" one time, and then for some reason it never prints "Finished reading data" until I manually end the client connection, after the client ends the connection the server prints everything OK. How can I make this code work? Thanks!
IO#gets is a blocking call. It waits for either a new line from the underlying I/O stream, or the end of the stream. (in which case it returns nil)
In server.rb you have
while line = session.gets
puts "Inside while loop"
test << line
end
session.gets reads one line from your client, prints some debug info and appends the line to test. It then attempts to read another line from the client.
Your client.rb however never sends a seconds line, nor does it close the stream. It sends a single line:
socket.puts "Sending data.." # Send data to server
and then waits for a response:
while(line = socket.gets)
puts line
end
which never comes because the server is sitting in the while loop, waiting for more data from the client.
You can solve this by calling close_write after all data has been sent:
socket.puts "Sending data.." # Send data to server
socket.close_write # Close socket for further writing
Calling close_write instead of close allows you to still read from the socket. It will also cause the server's session.gets to return nil, so it can get out of its loop.

Ruby background TCP Server

I wish to create a TCPServer which listens in the background while the script continues it's normal execution.
For example-
server=TCPServer.open(srvhost,srvport)
client=server.accept
res=send_request_cgi({
'uri' => "/",
'method' => 'GET',
'vars_get' =>
'cmd' => rand_text_alphanumeric(10)
}
})
print "#{res.headers}"
data=client.recv(1024)
puts data
client.close
This pauses the script at the listener.
Objective-The server will always receive a response on srvport which it should print.
Edit-
webserv=Thread.new do
server=TCPServer.new(srvhost,srvport)
client=server.accept
data=client.recv(1024)
if(data.empty?)
puts 'nope'
client.close
server.close
webserv.exit
end
puts data
client.write(cmd)
client.close
server.close
webserv.exit
end
You can put your server in a new Thread with
websrv = Thread.new do
<your server logic> # Main server loop, handle data, etc.
ensure
<your cleanup code> # Close connections, files, etc.
end
<application code>
websrv.join # wait for web server to do its thing
Needless to say, in ruby you'd usually wrap that in some sort of BackgroundServer class, but the basic idea is the same.
Note that rubys threads don't really execute in parallel; the interpreter just takes care of switching back and forth between them.

Multi-Threading in Ruby

I have a TCPserver that I made in ruby, the server seems to work, I can see that two or more clients can connect and be served by the server, but, they sometime get stuck (as in need to wait for the other client to disconnect or just get unresponsive), usually after the "pass_ok" bit, When connecting only with one client I don't see this issue.
Here is my code:
def self.main_server
begin
server = TCPServer.open(#port)
rescue Exception => e
CoreLogging.syslog_error("Cant start server: #{e}")
end
#main_pid = Process.pid
# Main Loop
Thread.abort_on_exception = true
while true
Thread.fork(server.accept) do |client|
#client = client
sock_domain, remote_port, remote_hostname, remote_ip = #client.peeraddr # Get some info on the incoming connection
CoreLogging.syslog_error("Got new connection from #{#client.peeraddr[3]} Handeled by Thread: #{Thread.current}") # Log incoming connection
#client.puts "Please enter password: " # Password testing (later will be from a config file or DB)
action = #client.gets(4096).chomp # get client password response 'chomp' is super important
if action == #password
# what to do when password is right
pass_ok
Thread.exit
else
# what to do when password is wrong
pass_fail
Thread.exit
end
end
begin
CoreLogging.syslog_error("Thread Ended (SOFT)")
rescue Exception => e
CoreLogging.syslog_error("Thread was killed (HARD)")
end
end
end
I'll leave it here for future reference and hope someone in a close situation will find it useful.
The issue was the global #client variable, which got overwritten every new thread and then inherited to the subclasses inside the thread.
using a local client variable (without the '#') got it to work as supposed.

Unusual behavior of Ruby script while working with sockets

The thing is that I am very beginner in Ruby programming and I have some troubles with understanding Ruby's behavior.
Here is the code:
require 'socket'
server = TCPServer.new 2000
loop do
Thread.start(server.accept) do |client|
puts 'Client connected: ' + client.gets
line = "-1"
while (line != "/close ")
line = client.gets();
puts line
end
puts 'client closed'
client.close
end
end
As you see, it's a simple socket server waiting for some input information.
The problem is that when it gets "/close ", it exits the while loop, but never goes to
puts 'client closed' and client.close. So why is it so, or what am I doing wrong?
I suspect that if you change puts line to puts line.inspect you will see the problem.
Found it? gets reads by lines, which includes the line-terminating \n, so no line you get will ever equal "/close ". Changing the comparison to use line.strip will work, for example.

Eventmachine: Escapes received binary data

I'm using EventMachine tutorial as a starting point to experiment with sending / receiving binary data. The sample code is:
#!/usr/bin/env ruby
require 'rubygems' # or use Bundler.setup
require 'eventmachine'
class EchoServer < EM::Connection
def receive_data(data)
puts data
send_data(data)
end
end
EventMachine.run do
# hit Control + C to stop
Signal.trap("INT") { EventMachine.stop }
Signal.trap("TERM") { EventMachine.stop }
EventMachine.start_server("0.0.0.0", 10000, EchoServer)
end
I use telent to connect to the EM server
telent -8 localhost:10000
I send the following data to the EM server:
\x17\xEB\xB3\b\x05\x00\x00\x00\x01\x00\x00\x89Bo\xAF
EM prints out this:
\\x17\\xEB\\xB3\\b\\x05\\x00\\x00\\x00\\x01\\x00\\x00\\x89Bo\\xAF\r\n
For some reason, it's escaping the slashes and adding \r\n to the received data.
How do I stop EM from escaping the data and just accept raw binary that's sent to it?
Turns out the culprit is telnet. I solved this using netcat.
The command is from this question: How to escape hex values in netcat
echo "\x17\xEB\xB3\b\x05\x00\x00\x00\x01\x00\x00\x89Bo\xAF" | nc localhost 10000

Resources