Detecting lost connections in TCPServer connection

(Peter Hickman) #1

I have some code similar to the following:

#!/usr/bin/env ruby

require 'socket'

server = TCPServer.new('localhost', 2000)

loop do
  Thread.start(server.accept) do |client|
    puts 'Client connects'

    loop do
      s = client.gets
      break if s.index('quit') == 0
      client.puts "Hello #{s}"
    end

    puts 'Client disconnects'

    client.close
  end
end

When this runs a new connection will spawn a new thread. If the user sends
the string starting with 'quit' then the connection will close

But if the use just terminates their session then the thread is left idle
waiting for input from a connection that no one is on the other end of.
Eventually I will end up with 100s of threads with no one to talk to

Is there a way for these threads to detect that the connection has been
lost? As opposed to someone just taking a very long time to communicate

0 Likes

(Marvin Gülker) #2

      s = client.gets
      break if s.index('quit') == 0
[...]
But if the use just terminates their session then the thread is left idle
waiting for input from a connection that no one is on the other end
of.

I don't think this is true. A client terminating a session means that
the client closes its side of the TCP stream. This is going to cause an
EOF on the server, and thus IO#gets should return `nil' if the
connection is closed. Thus, in your example code, the thread handling
the terminating client is going to crash with a NoMethodError exception
(`nil' does not support the #index method).

Quote from <https://ruby-doc.org/core-2.6.2/IO.html#method-i-gets>:

Returns nil if called at end of file.

Alternatively, there's IO#readline: <https://ruby-doc.org/core-2.6.2/IO.html#method-i-readline>:

Reads a line as with IO#gets, but raises an EOFError on end of file.

If an exception is more convenient for you than just checking for `nil'.

···

Am 26. März 2019 um 11:38 Uhr +0000 schrieb Peter Hickman:

--
Blog: https://mg.guelker.eu

0 Likes

(John W Higgins) #3

pry [1] is your friend

Add

require 'pry'

at the top of your code - add a

binding.pry

line just after your s = client.gets

Fire it up - connect and disconnect a client and see what happens - see
what the thread thinks about your disconnecting client and what it provides
you for information. There is at least one specific error with your code
and technically a better option than the loop ( while (s = client.gets)
.... ) - but the best option is to work your way through it by examining
what the code does.

John

[1] - https://github.com/pry/pry

···

On Tue, Mar 26, 2019 at 4:39 AM Peter Hickman < peterhickman386@googlemail.com> wrote:

I have some code similar to the following:

#!/usr/bin/env ruby

require 'socket'

server = TCPServer.new('localhost', 2000)

loop do
  Thread.start(server.accept) do |client|
    puts 'Client connects'

    loop do
      s = client.gets
      break if s.index('quit') == 0
      client.puts "Hello #{s}"
    end

    puts 'Client disconnects'

    client.close
  end
end

When this runs a new connection will spawn a new thread. If the user sends
the string starting with 'quit' then the connection will close

But if the use just terminates their session then the thread is left idle
waiting for input from a connection that no one is on the other end of.
Eventually I will end up with 100s of threads with no one to talk to

Is there a way for these threads to detect that the connection has been
lost? As opposed to someone just taking a very long time to communicate

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

0 Likes

(Peter Hickman) #4

I've tested this by counting the threads. This is what I've put before the
main loop

Thread.start do
  loop do
    puts "Thread count #{Thread.list.count}"
    sleep 5
  end
end

Damn. The demo code I gave does reduce the number of threads. The real
application (somewhat larger) does not

Ignore this thread :slight_smile: Marvin is correct, my error handling is eating up the
problem with the nil being returned

Well that was an easy fix. Thank you both for your help

0 Likes

(Ryan Davis) #5

You might want `Thread.abort_on_exception = true` to make these things more evident.

···

On Mar 26, 2019, at 07:05, Peter Hickman <peterhickman386@googlemail.com> wrote:

Ignore this thread :slight_smile: Marvin is correct, my error handling is eating up the problem with the nil being returned

0 Likes