Reconnect WebSocket client connection on no response - websocket

I have a lot of async connections to a WebSocket server. After a while, the server stops responding and the script just waits on response. I have to dockerize and deploy the script, so the only way to reset the connection is to run the docker image again.
The ideal solution would be something like a keyword argument to WebSockets.open. The code looks like:
map(container) do item
#async begin
WebSockets.open(url) do ws
payload = generatepayload(item)
if writeguarded(ws, JSON3.write(payload))
while isopen(ws)
data, success = readguarded(ws)
### process data
end
end
end
end
end
But any solution where I just have to write julia code would work.

WebSockets. jl's readguarded() allows a timeout read error exception to be ignored. As long as your socket read has a reasonable timeout (it should) handling the read failure yourself may help, since this could give you a new websocket connection with the server. for example:
map(container) do item
#async while true
try
WebSockets.open(url) do ws # reopens the websocket on while loop
payload = generatepayload(item)
if writeguarded(ws, JSON3.write(payload))
while isopen(ws)
data, success = read(ws)
### process data
end
end
end
catch y
#debug y # log something here
maybe check server some other way here and break if offline
end
end
end

Related

How to wait until TCP socket response is ready

I'm connecting to a TCP server using Ruby's TCPSocket class.
I send some data about an address and I must wait for the server to do some processing to give me the geocoding of said address. Since the process in the server takes some time, I cannot read the response immediately.
When I used socket.readpartial() I got a response of two white spaces.
I temporarily solved this using sleep(5) but I don't like this at all, because it is hackish and clumsy, and I risk that even after 5 seconds the response is not ready and I still get an empty response.
I know that the responses will always be 285 characters long.
Is there a more correct and elegant way of having my TCP socket wait for the full response?
Here's my code:
def matchgeocode(rua, nro, cidade, uf)
count = 0
begin
socket = TCPSocket.new(GEOCODER_URL, GEOCODER_PORT)
# Needed for authentication
socket.write("TICKET #{GEOCODER_TICKET}")
socket.read(2)
# Here's the message I send to the server
socket.write("MATCHGEOCODE -Rua:\"#{rua}\" -Nro:#{nro} -Cidade:\"#{cidade}\" -Uf:\"#{uf}\"")
# My hackish sleep
sleep(5)
# Reading the fixed size response
response = socket.readpartial(285)
socket.write('QUIT')
socket.close
rescue Exception => e
count += 1
puts e.message
if count <= 5 && response.eql?('')
retry
end
end
response
end
Since you know the length of the response you should use read, not readpartial.
readpartial returns immediately if ANY data is available, even one byte is enough. That's why you need the sleep call so that the response has time to return to you before readpartial tries to peek at what data is present.
read on the other hand blocks completely until ALL requested data is available. Since you know the length of the result then read is the natural solution here.

Ruby Socket Programming Waiting for Client Input

I am trying to implement a small script that will create a server, wait for a client to connect, create a new thread for the client and then process client requests. The first request from the client will be a HELO text and the server must respond with the HELO text followed by IP Address, Port Number and a Student Number. I have got this bit working.
The next request will be any random string and this must not return anything. I have got this bit working.
The last request will be a KILL_SERVICE request which must close the server. I am not sure how to do this. The other issue is that my program will only work with one command at a time. I do not know how to deal with one command and then wait for the next command from the client. My server code is below. Any help would be appreciated.
require 'socket'
port = 8888
puts "Starting Up Server"
server = TCPServer.open(port)
while (client = server.accept)
Thread.start do
input = client.gets
if input.start_with?("HELO")
client.puts "#{input}IP:#{client.peeraddr[2]}\nPort:#{port}\nStudentID:[2]\n"
elsif input == "KILL_SERVICE\n"
client.puts "KILL"
else
puts input
end
end
end
As soon as your code leaves the Thread.start block that thread is considered finished. If you want it to stick around you need to wrap it in a loop:
while (client = server.accept)
Thread.start do
client_running = true
while (client_running)
# ... Your code
end
end
end
Then, if any of those commands should stop the thread, set client_running = false and the loop will exit. To force shut-down the whole thing, exit(0) will end the process.
Update: Added block on how to handle shutting down the server itself, not just the connection.
You need to continue waiting for input from the client after accepting the connection.
while (client = server.accept)
Thread.start do
while input = client.gets
# process input
end
end
end
When you receive KILL_SERVICE you need to close the client using client.close and break out of the while loop using break. Once you've broken out of the while loop, the thread handling that client will exit.

Mocking a network connection

Just how about do I mock a network connection?
Suppose I am writing a client-server topology; the client is already in place, but I want to test that when a network connection is received on the server that an event is fired; or that a function is called in response to a network connection being received.
I'm currently using EventMachine, and I'd like to figure out how to unit test these interactions to make sure that mocked network connections/messages will be handled correctly without having to do integration testing and actually having to write a clien tthat would send the appropriate messages to the test interface. I hope this makes sense?
Basically, I want to be able to have a test for the Server to make sure it responds to mocked messages correctly without having to ever open a real internet connection or write a dedicated client just for testing - I would prefer to be able to be able to say 'This is the message I want to receive, now pretend i've received it from a real client and handle it'
I solved this simply by instantiating an instance of the connection class in EventMachine and tehn calling the method directly - not sure why I didn't think of that in the first place!
describe 'Network manager' do
it 'should call the ChangeStatus handler when it receives the ChangeStatus packet' do
# Arrange
connection = TcpConnection.new
# Set up the packet handler
packet_handler = double 'TcpPacketHandler'
# Inject dependency
connection.packet_handler = packet_handler
# Create the message
data = [ 15, # message code + payload size
0, # message code
4, # protocol version
12, # size of string
]
data.push 'hello, world'.bytes.to_a
reader, writer = IO.pipe
writer.write data
writer.close
puts data
# Assert
expect(packet_handler).to receive(:handle_message).with(data[1], anything()).once
message = reader.read
# Act
connection.receive_data message
end
end

How can I properly handle persistent TCP socket connections (to simulate an HTTP server)?

So, I'm trying to simulate some basic HTTP persistent connections using sockets and Ruby - for a college class.
The point is to build a server - able to handle multiple clients - that receives a file path and gives back the file content - just like an HTTP GET.
The current server implementation loops listening for clients, fires a new thread when there's an incoming connection and reads the file paths from this socket. It's very dumb, but it works fine when working with non-presistent connections - one request per connection.
But they should be persistent.
Which means the client shouldn't worry about closing the connection. In the non-persistent version the servers echoes the response and close the connection - goodbye client, farewell.
But being persistent means the server thread should loop and wait for more incoming requests until... well until there's no more requests. How does the server knows that? It doesn't! Some sort of timeout is needed. I tried to do that with Ruby's Timeout, but it didn't work.
Googling for some solutions - besides being thoroughly advised to avoid using Timeout module - I've seen a lot of posts about the IO.select method, that should handle this socket waiting issue way better than using threads and stuff (which really sounds cool, considering how Ruby threads (don't) work). I'm trying to understand here how IO.select works, but still wasn't able to make it work in the current scenario.
So I aske basically two things:
how can I efficiently work this timeout issue on the server-side, either using some thread based solution, low-level socket options or some IO.select magic?
how can the client side know that the server has closed its side of the connection?
Here's the current code for the server:
require 'date'
module Sockettp
class Server
def initialize(dir, port = Sockettp::DEFAULT_PORT)
#dir = dir
#port = port
end
def start
puts "Starting Sockettp server..."
puts "Serving #{#dir.yellow} on port #{#port.to_s.green}"
Socket.tcp_server_loop(#port) do |socket, client_addrinfo|
handle socket, client_addrinfo
end
end
private
def handle(socket, addrinfo)
Thread.new(socket) do |client|
log "New client connected"
begin
loop do
if client.eof?
puts "#{'-' * 100} end connection"
break
end
input = client.gets.chomp
body = content_for(input)
response = {}
if body
response.merge!({
status: 200,
body: body
})
else
response.merge!({
status: 404,
body: Sockettp::STATUSES[404]
})
end
log "#{addrinfo.ip_address} #{input} -- #{response[:status]} #{Sockettp::STATUSES[response[:status]]}".send(response[:status] == 200 ? :green : :red)
client.puts(response.to_json)
end
ensure
socket.close
end
end
end
def content_for(path)
path = File.join(#dir, path)
return File.read(path) if File.file?(path)
return Dir["#{path}/*"] if File.directory?(path)
end
def log(msg)
puts "#{Thread.current} -- #{DateTime.now.to_s} -- #{msg}"
end
end
end
Update
I was able to simulate the timeout behaviour using the IO.select method, but the implementation doesn't feel good when combining with a couple of threads for accepting new connections and another couple for handling requests. The concurrency makes the situation mad and unstable, and I'm probably not sticking with it unless I can figure out a better way of using this solution.
Update 2
Seems like Timeout is still the best way to handle this. I'm sticking with it till find a better option.
I still don't know how to deal with zombie client connections.
Solution
I endend up using IO.select (got inspired when looking at the webrick code). You cha check the final version here (lib/http/server/client_handler.rb)
You should implement something like heartbeat packets.Client side should send special packets to after few secs/mins to ensure that server doesn't time out the connection on the client end.You just avoid doing anything in this call.

Ruby TCPSocket doesn't notice it when server is killed

I've this ruby code that connects to a TCP server (namely, netcat). It loops 20 times, and sends "ABCD ". If I kill netcat, it takes TWO iterations of the loop for an exception to be triggered. On the first loop after netcat is killed, no exception is triggered, and "send" reports that 5 bytes have been correctly written... Which in the end is not true, since of course the server never received them.
Is there a way to work around this issue ? Right now I'm losing data : since I think it's been correctly transfered, I'm not replaying it.
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
sock = TCPSocket.new('192.168.0.10', 5443)
sock.sync = true
20.times do
sleep 2
begin
count = sock.write("ABCD ")
puts "Wrote #{count} bytes"
rescue Exception => myException
puts "Exception rescued : #{myException}"
end
end
When you're sending data your blocking call will return when the data is written to the TCP output buffer. It would only block if the buffer was full, waiting for the server to acknowledge receipt of previous data that was sent.
Once this data is in the buffer, the network drivers try to send the data. If the connection is lost, on the second attempt to write, your application discovers the broken state of the connection.
Also, how does the connection close? Is the server actively closing the connection? In which case client socket would be notified at its next socket call. Or has it crashed? Or perhaps there's a network fault which means you can no longer communicate.
Discovering a broken connection only occurs when you try to send or receive data over the socket. This is different from having the connection actively closed. You simply can't determine if the connection is still alive without doing something with it.
So try doing sock.recv(0) after the write - if the socket has failed this would raise "Errno::ECONNRESET: Connection reset by peer - recvfrom(2)". You could also try sock.sendmsg "", 0 (not sock.write, or sock.send), and this would report a "Errno::EPIPE: Broken pipe - sendmsg(2)".
Even if you got your hands on the TCP packets and get acknowledgement that the data had been received at the other end, there's still no guarantee that the server will have processed this data - it might in its input buffer but not yet processed.
All of this might help identify a broken connection earlier, but it still won't guarantee that the data was received and processed by the server. The only sure way to know that the application has processed your message is with an application level response.
I tried without the sleep function (just to make sure it wasn't putting on hold anything) and still no luck:
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'activesupport' # Fixnum.seconds
sock = TCPSocket.new('127.0.0.1', 5443)
sock.sync = true
will_restart_at = Time.now + 2.seconds
should_continue = true
while should_continue
if will_restart_at <= Time.now
will_restart_at = Time.now + 2.seconds
begin
count = sock.write("ABCD ")
puts "Wrote #{count} bytes"
rescue Exception => myException
puts "Exception rescued : #{myException}"
should_continue = false
end
end
end
I analyzed with Wireshark and the two solutions are exactly behaving identically.
I think (and can't be sure) that until you actually call your_socket.write (which will not fail as the socket is still opened because you weren't probing for its possible destruction), the socket won't raise any error.
I tried to simulate this with nginx and manual TCP sockets. And look at that:
irb> sock = TCPSocket.new('127.0.0.1', 80)
=> #<TCPSocket:0xb743b824>
irb> sock.write("salut")
=> 5
irb> sock.read
=> "<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body>\r\n</body>\r\n</html>\r\n"
# Here, I kill nginx
irb> sock.write("salut")
=> 5
irb> sock.read
=> ""
irb> sock.write("salut")
Errno::EPIPE: Broken pipe
So what's the conclusion from here? Unless you're actually expecting some data from the server, you're screwed to detect that you've lost the connection :)
To detect a gracefully close, you'll have to read from the socket - read returning 0 indicates the socket has closed.
If you do need know if data got sent successfully though, there's no way other than implementing ACKs of the data at the application level.

Resources