Read data both ways TCPServer Ruby - 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.

Related

Sending data to TCPServer more than one time

I'm new to ruby and I'm trying to make a client to connect to a TCPServer, and it seems that in order to do so I have to call the method close_write every time I finish sending data one way, to let the client/server know that the other end is finished sending data. Whenever I do that then Im not able to write info to the server or client again because the socket is not opened for writing anymore.
This is my code:
client.rb
require "socket"
socket = TCPSocket.open("localhost", 6666)
loop do
input = gets.chomp
socket.puts input # Send data to server
socket.close_write
while(line = socket.gets)
puts line
end # Print sever response
break if input=="EXIT"
end
socket.close
server.rb
require "socket"
server = TCPServer.new 6666
data = Hash.new { |hash, key| hash[key] = {} }
WAITING_SET_VALUE = "1"
WAITING_NEW_COMMAND = "0"
loop do
Thread.start(server.accept) do |session|
thread_status ||= WAITING_NEW_COMMAND
....
puts "Entering If..."
if(thread_status == WAITING_NEW_COMMAND) #Check thread status
puts "thread_status == WAITING_NEW_COMMAND"
puts "CHECKING COMMAND.."
case line.strip
when /^set \w* \w* \d{1,7} \d{1,7}$/
puts "COMMAND SET"
thread_status = WAITING_SET_VALUE
lineArr = line.strip.split(" ")
varName = lineArr[1]
flag = lineArr[2]
ttl = lineArr[3]
size = lineArr[4]
puts "END SET EXECUTION"
session.write "Executed"
session.close_write
...
Is there a way to open the socket for writing again, or a better way to do this back and forth connection between server and client without losing context? Thanks!
When designing a client-server protocol, you have to decide:
How a client knows when a response has more lines/data.
How a client knows when a response is complete.
How a client knows when a response is invalid/valid.
How a client knows when there was some type of server error.
A simple approach is for the server to return a response with the number of lines (as in the code below). However, instead, you could use END or something so that the client knows when there is no more data to read. I would strongly suggest looking into tutorials about Protocols.
Save the below into a file called client_server.rb. First, run the server with ruby ./client_server.rb s and then in a separate terminal run the client with ruby ./client_server.rb c. Next, type in list over and over to see the different responses. I added list so that you don't have to type in set w w 1 1 over and over for testing purposes. Check it out and let me know if you have any questions.
# frozen_string_literal: true
require 'socket'
# Use:
# First, run the server: ruby ./client_server.rb s
# Then, run the client: ruby ./client_server.rb c
# Use "netcat localhost 6666" on the command line to test
# without implementing a client.
# Returns false to close this client socket.
# Returns true to continue reading from this client socket.
def handle_client(client_id, client_socket, command)
# TODO: Define some type of client-server Protocol here.
case command
when /^set \w* \w* \d{1,7} \d{1,7}$/
puts "Running command for client #{client_id}: #{command}"
# This is just for testing purposes.
case rand(3)
when 0
client_socket.puts 'lines 0'
when 1
client_socket.puts 'lines 3'
client_socket.puts 'This is line 1.'
client_socket.puts 'This is line 2.'
client_socket.puts 'This is line 3.'
when 2
client_socket.puts 'The set command returned an error.'
end
when 'list'
puts "Responding to client #{client_id} with list of messages."
# This is just for testing purposes.
case rand(3)
when 0
client_socket.puts 'messages 0'
when 1
client_socket.puts 'messages 2'
client_socket.puts 'This is message 1.'
client_socket.puts 'This is message 2.'
when 2
client_socket.puts 'Unable to retrieve messages due to error.'
end
when 'bye'
puts "Killing client #{client_id}."
return false # Disconnect and kill the thread.
else
client_socket.puts "[ERROR] Invalid command: #{command}"
end
client_socket.flush # Flush all output just in case.
true # Continue connection.
end
case ARGV[0].to_s.downcase
when 's' # server
TCPServer.open(6666) do |server_socket|
global_client_id = 1
loop do
Thread.start(global_client_id, server_socket.accept) do |client_id, client_socket|
puts "Accepting new client #{client_id}."
loop do
command = client_socket.gets
if command.nil?
puts "Client #{client_id} disconnected manually."
break
end
command = command.strip
keep_alive = handle_client(client_id, client_socket, command)
break unless keep_alive
end
client_socket.close
end
global_client_id += 1
end
end
when 'c' # client
TCPSocket.open('localhost', 6666) do |socket|
puts '[Commands]'
puts 'set <word> <word> <num> <num> Run set command.'
puts 'list Get list of messages.'
puts 'exit, bye Exit the client.'
puts
loop do
print '> '
input = $stdin.gets.strip
# TODO: Define some type of client-server Protocol here.
case input
when /EXIT|BYE/i
socket.puts 'bye'
socket.flush
break
when /\Aset .+\z/
socket.puts input
socket.flush
response = socket.gets
if response.nil?
puts 'Server is not running anymore! Disconnecting.'
break
end
response = response.strip
match_data = response.match(/\Alines (?<lines>\d+)\z/)
if match_data
line_count = match_data[:lines].to_i
puts "Received #{line_count} lines from server."
1.upto(line_count) do |i|
line = socket.gets
puts ">> Resp[#{i}] #{line}"
end
else
# Can check for "response == 'ERROR'" or something.
puts "Server error or invalid response from server: #{response}"
end
when 'list'
socket.puts input
socket.flush
response = socket.gets
if response.nil?
puts 'Server is not running anymore! Disconnecting.'
break
end
response = response.strip
match_data = response.match(/\Amessages (?<messages>\d+)\z/)
if match_data
message_count = match_data[:messages].to_i
puts "Received #{message_count} messages from server."
1.upto(message_count) do |i|
line = socket.gets
puts ">> Resp[#{i}] #{line}"
end
else
# Can check for "response == 'ERROR'" or something.
puts "Server error or invalid response from server: #{response}"
end
else
puts "Invalid command: #{input}"
end
end
end
else
puts "Pass in 'c' for client or 's' for server."
end

Ruby socket keep waiting for Response

I am new to Ruby programming, I am trying to build a simple echo server, but on client.gets it freezes. I am new to socket programming.
server.rb
require "socket"
require "tty/prompt"
socket = TCPServer.open("0.0.0.0", 3000)
prompt = TTY::Prompt.new
loop do
client = socket.accept
client.print prompt.ask("server >")
line = client.gets # freezes here
puts line
end
socket.close
client.rb
require "socket"
require "tty/prompt"
socket = TCPSocket.open("192.168.1.6", 3000)
prompt = TTY::Prompt.new
loop do
socket.print prompt.ask("client >")
puts socket.gets # freezes here
end
server.close
Values you get from prompt.ask, they don't include newline char. And you don't add one when you print data to the socket. So gets waits for end-of-line marker that never arrives (hence "freezing").
Solution: use socket.puts instead of socket.print (does the same thing, but adds a newline char at the end).

how to use the ruby analytical receive binary stream from the TCP

I'm going to the receiving device over the data, but these data are binary stream, I put these data storage, then read to display them correctly, is there a better way?
require 'socket'
server = TCPServer.open(2000)
loop {
Thread.start(server.accept) do |client|
File.open("tmp","w") { |file| file.write(client.gets)}
File.open("tmp").each do |f|
puts f.unpack('H*')
end
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
end
}
the received data like this: xx^Q^A^Hb0# <90>26 2^B^#<83>ev
I want like this: 787811010862304020903236202032020001c26c0d0a
sorry about my poor English!
Using a temporary file with a name will cause a problem if there are multiple clients sending data; the temporary file will be overwritten.
You don't need to use a temporary file.
require 'socket'
server = TCPServer.open(2000)
loop {
Thread.start(server.accept) do |client|
puts client.gets.unpack('H*')
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close
end
}

Ruby TCP Client Server

I am working on a project where I have implemented a TCP client server for a device communication. In order to send a command from the server to the client, I am building a command that the device understands and sending to it but the response is not what should be returned
while 1
Thread.start(#otd.accept) do |client|
loop do
command_to_send ="<R-2,3,4>"
client.puts command_to_send
puts "Command #{command_to_send}sent"
#sleep 2
response = **client.gets** # here it halts and never puts the the next statement.
puts "Reponse #{response}"
end # end of nested loop
client.close
end #END OF THREAD.
end #end of while loop
Can someone tell me what I am missing?
Do not use gets as it expects '\n' to be a delimiter of the message.
Instead use: recv here is a method that could help you:
def read(timeout = 2, buffer = 1024)
message = ''
begin
Timeout::timeout(timeout) do
buffer = client.recv(buffer)
message += buffer
end
rescue Timeout::Error
puts "Received nothing from client: #{client.__id__}"
message = ''
rescue Exception => e
raise "Client failed to read for reason - #{e.message}"
end
message
end
You do not have to use sleep anymore as recv like gets is blocking. But the timeout makes sure you are not stuck waiting for a response not existing.

How can I only read one line of data from a TCPSocket in 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!

Resources