How to close a TCP socket? (TCPSocket#close doesn't close it)

Hi, perhaps I miss something but when I close a TCP connection using
TCPSocket#close the connection remains as OS level for long seconds
(minutes).

Be reading the doc:

IO#close

Closes ios and flushes any pending writes to the operating system. The
stream is unavailable for any further data operations; an IOError is
raised
if such an attempt is made. I/O streams are automatically closed when
they
are claimed by the garbage collector.

So, does it mean that the TCP connection is really terminated when the
garbage collector removes it (some seconds/minutes after calling
TCPSocket#close)?

Unfortunatelly it’s not valid for me, I need the capability to close a
TCP
connection and open a new one without mantaining two parallel
connections.

Any other way to really close a TCP connection at OS level? Thanks.

On Tue, Jan 12, 2010 at 11:43 AM, Iñaki Baz C. [email protected] wrote:

are claimed by the garbage collector.

So, does it mean that the TCP connection is really terminated when the
garbage collector removes it (some seconds/minutes after calling
TCPSocket#close)?

The TCP connection is terminated when you call TCPSocket#close.

Unfortunatelly it’s not valid for me, I need the capability to close a TCP
connection and open a new one without mantaining two parallel connections.

Any other way to really close a TCP connection at OS level? Thanks.

Why do you need to do that? Your connection is in the TIME_WAIT state,
which is exists for good reasons. There are no parallel connections.

El Martes, 12 de Enero de 2010, Lars C.
escribió:> > stream is unavailable for any further data operations; an IOError is

raised if such an attempt is made. I/O streams are automatically closed
when they are claimed by the garbage collector.

So, does it mean that the TCP connection is really terminated when the
garbage collector removes it (some seconds/minutes after calling
TCPSocket#close)?

The TCP connection is terminated when you call TCPSocket#close.

Yes, I’m realizing of it right now. There must be some error in my code
which
prevents the socket from being closed. I must investigate it.

Unfortunatelly it’s not valid for me, I need the capability to close a
TCP connection and open a new one without mantaining two parallel
connections.

Any other way to really close a TCP connection at OS level? Thanks.

Why do you need to do that? Your connection is in the TIME_WAIT state,
which is exists for good reasons. There are no parallel connections.

In my buggy script the connection remains ESTABLISHED, but as I said
above it
must be my fault somewhere in the script.

However, by trying just:

require “socket”
s = TCPSocket.new(server, port)
s.close

After “s.close” the connection is ended totally, no TIME_WAIT status.

Thanks a lot.

On Tue, Jan 12, 2010 at 12:26 PM, Iñaki Baz C. [email protected] wrote:

stream is unavailable for any further data operations; an IOError is
prevents the socket from being closed. I must investigate it.

In my buggy script the connection remains ESTABLISHED, but as I said above it
must be my fault somewhere in the script.

However, by trying just:

require “socket”
s = TCPSocket.new(server, port)
s.close

After “s.close” the connection is ended totally, no TIME_WAIT status.

Take a look at this tutorial:

http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html

There’s a diagram explaining all the states for a TCPSocket, and
explains the reaons for the TIME_WAIT status.

Hope this helps,

Jesus.

El Martes, 12 de Enero de 2010, Iñaki Baz C.
escribió:

The TCP connection is terminated when you call TCPSocket#close.

Yes, I’m realizing of it right now. There must be some error in my code
which prevents the socket from being closed. I must investigate it.

I’ve found the cause of my issue. My ruby script listens into a pipe and
when
a message is received it’s sent via the TCPSocket.
Also there is a thread which does @socket.gets("\r\n") to receive the
responses. In this way I can I can send multiple requests to the server
without waiting for the responses. Instead the thread doing “gets” reads
the
responses, parses a request identifier and that’s all.

But in this case when calling socket.close the connection is not
terminated. A
simplified code:

require “socket”
@socket = TCPSocket.new(server, port)

Thread.new do
res = @socket.gets("\r\n")
end

@socket.close
sleep 120

When “@socket.close” is called it’s just closed “at Ruby level”, this
is, I
cannot write/read from it as I get “closed pipe” error (which makes
sense).

However the TCP connection remains open at OS level (in ESTABLISHED
status as
netstat shows in both the client and server).

But it’s really interesting (for me) the following fact:

Without the line @socket.close netstat output shows (during the sleep
120):

tcp 0 0 192.168.1.10:51112 88.231.79.226:5062 ESTABLISHED
23814/irb

But when running the script with @socket.close line then I see:

tcp 0 0 192.168.1.10:51112 88.231.79.226:5062 ESTABLISHED -

It’s like if the Ruby process (irb) has detached itself from the socket
(no
process pid in the netstat output for that connection), but the socket
remains
open at OS level.

Is it the expected behavior?

Thanks.

El Martes, 12 de Enero de 2010, Jesús Gabriel y
Galán escribió:

After “s.close” the connection is ended totally, no TIME_WAIT status.

Take a look at this tutorial:

http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html

There’s a diagram explaining all the states for a TCPSocket, and
explains the reaons for the TIME_WAIT status.

Thanks a lot, but as I’ve explained I don’t see TIME_WAIT, my issue is
different (described in other mail sent right now).

Thanks.

Iñaki Baz C. wrote:

But in this case when calling socket.close the connection is not
terminated. A
simplified code:

require “socket”
@socket = TCPSocket.new(server, port)

Thread.new do
res = @socket.gets(“\r\n”)
end

@socket.close
sleep 120

When I run this code, substituting a ‘www.google.com’ and port 80, I see
the socket in TIME_WAIT state if I go to another terminal.

This is with
ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
built from source, running under Ubuntu Hardy.

It appears to be consistent: i.e. I can ctrl-C out of the program and
start it again, and each time I get another TIME_WAIT socket.

Whilst what you’re doing is probably allowed, it’s a bit ugly: one
thread is waiting to read data from a socket at the same time as another
closes it.

There is a more graceful way which might be useful. In the writing
thread you can half-close the socket (@socket.close_write). If the other
side notices this and then closes from its side, you can then finish
reading what it sends you and close the read side.

require “socket”
@socket = TCPSocket.new(‘smtp.example.com’, 25)

Thread.new do
while res = @socket.gets(“\r\n”)
STDERR.puts res
end
@socket.close_read
end

@socket.close_write
sleep 120

Anyway, going back to your original code: to isolate the behaviour
you’ve seen, I suggest you set up a ruby server and client pair of
processes and connect between them (preferably using localhost). This
makes it completely standalone and easy to reproduce by others, and
potentially easy to fix if it is in fact a bug. Post your ruby version
and platform too.

Regards,

Brian.

El Martes, 12 de Enero de 2010, Brian C. escribió:

When I run this code, substituting a ‘www.google.com’ and port 80, I see
the socket in TIME_WAIT state if I go to another terminal.

This is with
ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
built from source, running under Ubuntu Hardy.

It appears to be consistent: i.e. I can ctrl-C out of the program and
start it again, and each time I get another TIME_WAIT socket.

I really sorry, I was doing a “grep ESTABLISHED” in my netstat
command…
Of course the TIME_WAIT appears after closing the connection :slight_smile:
Sorry.

@socket = TCPSocket.new(‘smtp.example.com’, 25)

Thread.new do
while res = @socket.gets(“\r\n”)
STDERR.puts res
end
@socket.close_read
end

@socket.close_write
sleep 120

Yes, this works much better :slight_smile:
Thanks a lot.