Ruby to read from a Server that resets frequently - ruby

I have a server that disconnects frequently
$echo "GET hosts" | nc localhost 323
a.host.com
b.host.com
c.host.com
While reading the same thing from ruby with following code
s = TCPSocket.new(host,port)
s.puts "GET hosts\n\n\r\r"
data = ""
begin
until s.closed?
l = s.gets
puts "Host:" + l
data = data + l
end
rescue Exception => e
puts "pp" + e.message
end
prints out
Host:a.host.com
Host:b.host.com
Host:c.host.com
Host:Error reading from 3: Connection reset by peer
And the application hangs, somehow.
Any heads up for this?? Weird thing is that it is not entering the exception handler.

You can try rescuing like that rescue Errno::ECONNRESET => e.
Check out How to catch error Connection reset by peer (Errno::ECONNRESET), especially the retry bit.

Related

In RoR, how do I catch an exception if I get no response from a server?

I’m using Rails 4.2.3 and Nokogiri to get data from a web site. I want to perform an action when I don’t get any response from the server, so I have:
begin
content = open(url).read
if content.lstrip[0] == '<'
doc = Nokogiri::HTML(content)
else
begin
json = JSON.parse(content)
rescue JSON::ParserError => e
content
end
end
rescue Net::OpenTimeout => e
attempts = attempts + 1
if attempts <= max_attempts
sleep(3)
retry
end
end
Note that this is different than getting a 500 from the server. I only want to retry when I get no response at all, either because I get no TCP connection or because the server fails to respond (or some other reason that causes me not to get any response). Is there a more generic way to take account of this situation other than how I have it? I feel like there are a lot of other exception types I’m not thinking of.
This is generic sample how you can define timeout durations for HTTP connection, and perform several retries in case of any error while fetching content (edited)
require 'open-uri'
require 'nokogiri'
url = "http://localhost:3000/r503"
openuri_params = {
# set timeout durations for HTTP connection
# default values for open_timeout and read_timeout is 60 seconds
:open_timeout => 1,
:read_timeout => 1,
}
attempt_count = 0
max_attempts = 3
begin
attempt_count += 1
puts "attempt ##{attempt_count}"
content = open(url, openuri_params).read
rescue OpenURI::HTTPError => e
# it's 404, etc. (do nothing)
rescue SocketError, Net::ReadTimeout => e
# server can't be reached or doesn't send any respones
puts "error: #{e}"
sleep 3
retry if attempt_count < max_attempts
else
# connection was successful,
# content is fetched,
# so here we can parse content with Nokogiri,
# or call a helper method, etc.
doc = Nokogiri::HTML(content)
p doc
end
When it comes to rescuing exceptions, you should aim to have a clear understanding of:
Which lines in your system can raise exceptions
What is going on under the hood when those lines of code run
What specific exceptions could be raised by the underlying code
In your code, the line that's fetching the content is also the one that could see network errors:
content = open(url).read
If you go to the documentation for the OpenURI module you'll see that it uses Net::HTTP & friends to get the content of arbitrary URIs.
Figuring out what Net::HTTP can raise is actually very complicated but, thankfully, others have already done this work for you. Thoughtbot's suspenders project has lists of common network errors that you can use. Notice that some of those errors have to do with different network conditions than what you had in mind, like the connection being reset. I think it's worth rescuing those as well, but feel free to trim the list down to your specific needs.
So here's what your code should look like (skipping the Nokogiri and JSON parts to simplify things a bit):
require 'net/http'
require 'open-uri'
HTTP_ERRORS = [
EOFError,
Errno::ECONNRESET,
Errno::EINVAL,
Net::HTTPBadResponse,
Net::HTTPHeaderSyntaxError,
Net::ProtocolError,
Timeout::Error,
]
MAX_RETRIES = 3
attempts = 0
begin
content = open(url).read
rescue *HTTP_ERRORS => e
if attempts < MAX_RETRIES
attempts += 1
sleep(2)
retry
else
raise e
end
end
I would think about using a Timeout that raises an exception after a short period:
MAX_RESPONSE_TIME = 2 # seconds
begin
content = nil # needs to be defined before the following block
Timeout.timeout(MAX_RESPONSE_TIME) do
content = open(url).read
end
# parsing `content`
rescue Timeout::Error => e
attempts += 1
if attempts <= max_attempts
sleep(3)
retry
end
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.

Ruby TCPServer: Accepting a known socket

I have a circumstance where my server may close TCPServer and restart, saving all the users to a file, and immediately reloading them; their connections do not sever.
The problem is I can't seem to reinitialize their streams.
When we restart (and attempt to maintain connections), I reinitialize TCPServer, and load my array of connected users – Since these each have an existing socket address, stored as <TCPSocket:0x00000000000000>, can I reinitialize these addresses with TCPServer?
Normally, each user connects and is accepted:
$nCS = TCPServer.new(HOST, PORT)
begin
while socket = $nCS.accept
Thread.new( socket ) do |sock|
begin
d = User.new(sock)
while sock.gets
szIn = $_.chomp
DBG( "Received '" + szIn + "' from Client " + sock.to_s )
d.parseInput( szIn )
end
rescue => e
$stdout.puts "ERROR: Caught error in Client Thread: #{e} \r\n #{e.backtrace.to_s.gsub(",", ",\r\n")}"
sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
d.closeConnection
end
end
end
rescue => e
$stdout.puts "ERROR: Caught error in Server Thread: #{e} \r\n #{e.backtrace.to_s.gsub(",", ",\r\n")}"
exit
end
To give it a command to hot reboot, we use exec('./main --copyover') to flag that a copy over is occurring.
If $connected holds an array of all users, and each user has a socket, how do I reinitialize the socket that was open before the restart (assuming the other end is still connected)?
I suspect that using exec("./main", "--copyover", *$nCS, *$connected) is getting me closer, since this simply replaces the process, and should maintain the files (not close them).
You can't. The socket is only valid for the lifetime of the process: it is closed by the OS when the process exits. That in turn invalidates the connection, so the other end is not still connected.
How to Hot-Reboot a TCPServer in Ruby
Hot-Rebooting (aka Copyover) is a process by which an administrator can reload the application (along with any new changes made since last boot) without losing the client connections. This is useful in managing customer expectations as the application does not need to suffer severe downtime and disruption if in use.
What I propose below may not be the best practice, but it's functioning and perhaps will guide others to a similar solution.
The Command
I use a particular style of coding that makes use of command tables to find functions and their accessibility. All command functions are prefixed with cmd. I'll clean up the miscellany to improve readability:
def cmdCopyover
#$nCS is the TCPServer object
#$connected holds an array of all users sockets
#--copyover flags that this is a hot reboot.
connected_args = $connected.map do |sock|
sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
sock.fileno.to_s
end.join(",")
exec('./main.rb', '--copyover', $nCS.fileno.to_s, connected_args)
end
What we're passing are strings; $nCS.fileno.to_s provides us the file descriptor of the main TCPServer object, while connected_args is a comma-delineated list of file descriptors for each user connected. When we restart, ARGV will be an array holding each argument:
ARGV[0] == "--copyover"
ARGV[1] == "5" (Or whatever the file descriptor for TCPServer was)
ARGV[2] == "6,7,8,9" (Example, assuming 4 connected users)
What To Expect When You're Expecting (a Copyover)
Under normal circumstances, we may have a basic server (in main.rb that looks something like this:
puts "Starting Server"
$connected = Array.new
$nCS = TCPServer.new("127.0.0.1",9999)
begin
while socket = $nCS.accept
# NB: Move this loop to its own function, threadLoop()
Thread.new( socket ) do |sock|
begin
while sock.gets
szIn = $_.chomp
#do something with input.
end
rescue => e
puts "ERROR: Caught error in Client Thread: #{e}"
puts #{e.backtrace.to_s.gsub(",", ",\r\n")}"
sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
sock.close
end
end
end
rescue => e
puts "Error: Caught Error in Server Thread: #{e}"
puts "#{e.backtrace.to_s.gsub(",", ",\r\n")}"
exit
end
We want to move that main loop to its own function to make it accessible -- our reconnecting users will need to be reinserted in the loop.
So let's get main.rb ready for accepting a hot reboot:
def threadLoop( socket )
Thread.new( socket ) do |sock|
begin
while sock.gets
szIn = $_.chomp
#do something with input.
end
rescue => e
puts "ERROR: Caught error in Client Thread: #{e}"
puts #{e.backtrace.to_s.gsub(",", ",\r\n")}"
sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
sock.close
end
end
end
puts "Starting Server"
$connected = Array.new
if ARGV[0] == '--copyover'
$nCS = TCPServer.for_fd( ARGV[1].to_i )
$nCS.close_on_exec = false if $nCS.respond_to?(:close_on_exec=)
connected_args = ARGV[2]
connected_args.split(/,/).map do |sockfd|
$connected << sockfd
$connected.each {|c| threadLoop( c ) }
else
$nCS = TCPServer.new("127.0.0.1",9999)
$nCS.close_on_exec = false if $nCS.respond_to?(:close_on_exec=)
end
begin
while socket = $nCS.accept
threadLoop( socket )
end
rescue => e
puts "Error: Caught Error in Server Thread: #{e}"
puts "#{e.backtrace.to_s.gsub(",", ",\r\n")}"
exit
end
Caveat
My actual usage was a lot more ridiculously complicated, so I did my best to strip out all the garbage; however, I was realizing when I got the end here that you could probably do without $connected (it's a part of a larger system for me). There may be some errors, so please comment if you find them and I'll correct.
Hope this helps anyone who finds it.

Ruby - Handling null objects properly

In ruby, I'm not quite sure how to handle whether objects are nil or not.
For example I have the following:
begin
sp = SerialPort.new(#serial_device, #serial_bps, #serial_par, #serial_bits, SerialPort::NONE)
tcp = TCPSocket.new(#host, #port)
if (sp)
sp.print(command)
sp.close
elsif
tcp.print(command)
tcp.close
end
say siri_output
rescue
pp $!
puts "Sorry, I encountered an error: #{$!}"
ensure
request_completed
end
The problem is that the first object returns an error relating to:
No route to host - connect(2)
Which is correct, because TCP isn't connected duh. So I'd like it to use the next object instead.
Is there a way to do this without using certain Exceptions, I was wondering if there's a better way of doing what I'm after any how.
The problem is not the checks, you are doing that right. Anything that's not nil or false is true in ruby. It's that when you get an exception on the row that starts with "sp = .." the execution jumps to the resque block. You should restructure the code like this (I've removed the ensure clause because I do not know what it does). A good thing to do it's to rescue every specific type of exception in it's own row. by class name ex. NoConnectivityException => e (or what the class of the exception would be).
begin
sp = SerialPort.new(#serial_device, #serial_bps, #serial_par, #serial_bits, SerialPort::NONE)
sp.print(command)
sp.close
say siri_output
rescue Exception => e
puts "Sorry, I encountered an error: #{e.inspect}"
puts "trying TCP"
begin
tcp = TCPSocket.new(#host, #port)
tcp.print(command)
tcp.close
say siri_output
rescue Exception => e
puts "Sorry, I encountered an error: #{e.inspect}"
end
end
For quick and sloppy programming you can do another thing, but it's not recommended and generally a pain to debug, as any error results in nil and is silenced.
sp = SerialPort.new(#serial_device, #serial_bps, #serial_par, #serial_bits, SerialPort::NONE) rescue nil
tcp = TCPSocket.new(#host, #port) rescue nil
This way you'd end up with either a SerialPort object or nil object in the sp variable, and the same for sp.

Socket in Ruby blindly hangs when trying to check an offline server

I use the following code to check the server status of a certain game server to see if the game server is online.
begin
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(game_server.gameserver_port, game_server.gameserver_hostname)
sock.connect(sockaddr)
server_status.status = 1
rescue
server_status.status = 0
end
However it seems that the code blindly hangs up on the line without proceeding anywhere sock.connect(sockaddr) and does not throw an error when there's no services listening on that port. Is there a better way to do this in Ruby?
Could timeout be a good solution?
require 'timeout'
begin
timeout(5) do
# socket stuff...
end
rescue Timeout::Error
puts "Timed out!"
end

Resources