Ruby TCPSocket does not notice it when the server is killed

I have this ruby โ€‹โ€‹code that connects to a TCP server (namely netcat). He sings 20 times and sends "ABCD". If I kill netcat, it takes two iterations of the loop to throw an exception. In the first cycle, after netcat is killed, no exception will be thrown and "send" reports that were correctly written in 5 bytes ... In the end, this is incorrect, because, of course, the server never received them.

Is there any way around this problem? Right now I am losing data: since I think it is correctly transmitted, I will not reproduce 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 
+4
source share
3 answers

When you send data, your blocking call will return when data is written to the TCP output buffer. It only blocks the filling of the buffer, waiting for the server to confirm receipt of previously sent data.

As soon as the data is in the buffer, the network drivers will try to send the data. If the connection is lost, in the second recording attempt, your application detects a failed connection status.

Also, how does the connection close? Is the server actively closing the connection? In this case, the client socket will be notified the next time the socket is called. Or did he crash? Or maybe there is a network error, which means that you can no longer communicate.

Faulty connection detection occurs only when trying to send or receive data via a socket. This is different from the fact that the connection is actively closed. You simply cannot determine if the connection is alive without doing something with it.

So, try to make sock.recv(0) after recording - if the socket fails, it will raise " Errno::ECONNRESET: Connection reset by peer - recvfrom(2) ". You can also try sock.sendmsg "", 0 (not sock.write, or sock.send) and this will Errno::EPIPE: Broken pipe - sendmsg(2) " Errno::EPIPE: Broken pipe - sendmsg(2) ".

Even if you have hands on TCP packets and receive confirmation that the data was received from the other end, there is still no guarantee that the server will process this data - it can be in its input buffer but not yet processed.

All this can help identify a damaged connection earlier, but it still does not 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 through an application-level response.

+3
source

I tried without the sleep function (just to make sure this doesn't overlap anything) and still have 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 exactly behave the same.

I think (and cannot be sure) that unless you actually call your_socket.write (which will not fail as the socket is still open because you are not investigating its possible destruction ), the socket will not raise any error.

I tried to simulate this using nginx and manual TCP sockets. And look at this:

 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 is the conclusion? If you are not really expecting any data from the server, you are screwed to find that you have lost the connection :)

+1
source

To find the elegantly closed, you will need to read from the socket - reading returns 0 means that the socket is closed.

If you really need to know if the data was sent successfully, there is no other way than implementing the application ACK data.

+1
source

Source: https://habr.com/ru/post/1305316/


All Articles