[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Socket Question - close

Zach Dennis

7/7/2005 5:28:00 PM

If you have two sockets connected to eachother. Let's say...
a_socket and b_socket

And you tell a_socket to close, why does b_socket not know it is closed?

a_socket.close
b_socket.closed? => false
b_socket.puts "test"
Errno::EINVAL: Invalid argument
from (irb):4:in `write'
from (irb):4:in `puts'
from (irb):4

Am I missing something. Thanks,

Zach





6 Answers

Markus Koenig

7/7/2005 8:09:00 PM

0

Zach Dennis schrieb:
Zach Dennis wrote:

> If you have two sockets connected to eachother. Let's say...
> a_socket and b_socket
>
> And you tell a_socket to close, why does b_socket not know it is closed?

Because there might be some data left in b_socket's input buffer:

a_socket.puts 'Hello world!'
a_socket.close

If b_socket.closed? would be true now, b_socket.gets wouldn't return
"Hello world!", which would be weird. After all, the underlying file
descriptor is still open.

Greets,
Markus

Yohanes Santoso

7/7/2005 8:30:00 PM

0

Zach Dennis <zdennis@mktec.com> writes:

> If you have two sockets connected to eachother. Let's say...
> a_socket and b_socket
>
> And you tell a_socket to close, why does b_socket not know it is closed?
>
> a_socket.close
> b_socket.closed? => false

closed? is true only if you have called close() previously, i.e. if
you have closed the socket locally.

If the remote end closes its side, closed? would still return false if
you haven't called close() yet.

Detecting if the remote end has closed its side is not that
straight-forward.

Consider the following cases with TCP connection:

1. Your OS receives the FIN packet generated when the remote end does
a close(). select() will say that there is data to be read (the FIN
packet), and when you do a read, it will return with 0 data (since the
FIN packet is a transport-level business and does not contain
app-level data). if you try to write to such socket, you'll get EPIPE.

2. No FIN packet received because, say, the cable was hacked with an
axe. If you try to do a read(), read() will block until TCP times out
(2 hours?), same story with write(). That's because it is not possible
to determine if the cable has been chopped from software; for all the
TCP stack knows, it could have been just a network delay when it
doesn't immediately get any reply.

There is no rescuing for case #2, but case #1 is simply BSD socket
semantic. The BSD socket API does not have a mechanism to proactively
see if a FIN has been received. The mechanism must be triggered from
the calling app.

There may be mistakes in the above description as TCP is quite complex
and I don't profess full understanding of its intricacies. But I think
the broad idea is correct.

YS.


Yohanes Santoso

7/7/2005 9:04:00 PM

0

Yohanes Santoso <ysantoso-rubytalk@dessyku.is-a-geek.org> writes:

> Detecting if the remote end has closed its side is not that
> straight-forward.

Let me add to the above statement.

It is not straight forward for TCP connection. Other transport-level
protocol may have other mechanism to reliably detect and indicate
connection termination.

Due to difficulty in detecting connection termination in TCP,
app-level protocols running on top of TCP often has their own
mechanisms:

1. Implicit termination signal, e.g. HTTP/1.0: you send a request, the
server returns the length of a response and then the response
itself. Both the client and server knows that after the response is
done, the connection is considered terminated.

2. Explicit termination signal, e.g. FTP: the client must tell the
server that the connection is going to be terminated immediately.

3. Timed termination signal: if there is no activity for a certain
duration, the connection is considered terminated. Usually used in
app-level time-out mechanism.

YS.


David Brady

7/7/2005 10:09:00 PM

0

Yohanes Santoso wrote:

>It is not straight forward for TCP connection. Other transport-level
>protocol may have other mechanism to reliably detect and indicate
>connection termination.
>
>
I use some undocumented side-effects to detect if the remote has closed
the connection. On a TCPSocket, I find that select will immediately
return the socket once the remote has closed, but socket#gets will
return nil. In any valid transmit condition, if select returns the
socket, then gets will return valid data.

A simple reader loop, then, might look like this:

------------------------------------------------------------
sock = TCPSocket.new(server, port)
while true
if select( [sock], nil, nil, 0 )
data = sock.gets
break unless data
puts data
end
sleep 0.1
end

puts "Remote closed connection."
sock.close
------------------------------------------------------------

I'm not a Ruby expert by any means. I'm posting this for two reasons:

1. It seems to work for me and may help the OP.

2. I'm relying on undocumented behavior, and don't know of a good way to
robustly test this. I'm curious to know if anyone is aware of problems
with my solution, and to hear any ideas on how to unit test this.

Cheers,

-dB

--
David Brady
ruby-talk@shinybit.com
I'm having a really surreal day... OR AM I?



Yohanes Santoso

7/7/2005 10:37:00 PM

0

David Brady <ruby_talk@shinybit.com> writes:

> Yohanes Santoso wrote:
>
>>It is not straight forward for TCP connection. Other transport-level
>>protocol may have other mechanism to reliably detect and indicate
>>connection termination.
>>
>>
> I use some undocumented side-effects to detect if the remote has
> closed the connection. On a TCPSocket, I find that select will
> immediately return the socket once the remote has closed, but
> socket#gets will return nil. In any valid transmit condition, if
> select returns the socket, then gets will return valid data.

This is a documented behaviour. I covered this in my previous post:

<previous post>
1. Your OS receives the FIN packet generated when the remote end does
a close(). select() will say that there is data to be read (the FIN
packet), and when you do a read, it will return with 0 data (since the
FIN packet is a transport-level business and does not contain
app-level data). if you try to write to such socket, you'll get EPIPE.
</previous post>

Basically, you don't have to select(), just a read() is enough to read
in the FIN packet and trigger the TCP stack to realise the socket has
been closed by the other side. The select() is there to emphasize
there is data to be read, although that data is not app-level
data. Each time you do a non-zero read() and got a nil, then it means
the other side has closed down.

So, don't worry about using this. Use this liberally :)

YS.


Zach Dennis

7/8/2005 2:14:00 PM

0

Thank you all for you insight and input into this. This has been a great
help into my understanding of how sockets, eg, tcp sockets, work.

Zach