Puzzled about Ruby's behaviour re. closed sockets


#1

Everyone,

I’d like to write a test that ensures a Server application correctly
handles closed client connections – more specifically: I want to set
up a TCPServer, then a client, then close the client, then attempt to
write on the client Connection from server side – and make sure no
Error gets fired.

To my great surprise, the test doesn’t behave in any predictible way
– sometimes it passes, sometimes it fails.

I’ve written a piece of code that illustrates it:

require ‘socket’

consecutive_puts_to_attempt = 1
ok_counter = 0
error_counter = 0

10000.times do
server = TCPServer.open ‘127.0.0.1’, 4807
client = TCPSocket.open ‘127.0.0.1’, 4807
connection = server.accept
client.close

begin
consecutive_puts_to_attempt.times {connection.puts “Hello!”}
ok_counter += 1
rescue
error_counter += 1
end
server.close
end

puts “(Unexpectedly) passed #{ok_counter} times;”
puts “(correctly) failed #{error_counter} times.”

Statistically, this piece of code unexpectedly passes with no raised
error about 98% of the time. Errors are Errno::EPIPE and
Errno::ECONNRESET

When I fiddle with consecutive_puts_to_attempt value, the number of
expected raised errors changes –

puts attempts chances of raised error
1 2%
2 98%
3 98.5%
4 99%

Here is my environment:
ruby -v

ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.1]

Any idea of what’s going on?
Any suggestion about how I could make this piece of code predictible?
– Emmanuel.


#2

2007/7/29, Emmanuel G. removed_email_address@domain.invalid:

I’d like to write a test that ensures a Server application correctly
handles closed client connections – more specifically: I want to set
up a TCPServer, then a client, then close the client, then attempt to
write on the client Connection from server side – and make sure no
Error gets fired.

To my great surprise, the test doesn’t behave in any predictible way
– sometimes it passes, sometimes it fails.

Any idea of what’s going on?
Any suggestion about how I could make this piece of code predictible?

I guess it’s a combination of timing and buffering issue. With a
modified version I get

RKlemme@padrklemme1 ~
$ ruby ruby/socket-test-1.rb
(Unexpectedly) passed 2 times;
{Errno::ECONNABORTED=>998}

(see attachment)

Kind regards

robert


#3

Robert,

Thanks for your answer.

With a modified version I get

(Unexpectedly) passed 2 times;
{Errno::ECONNABORTED=>998}

I’ve tried your modified version. Here is a typical output when
attempting once to write in the closed pipe :

(Unexpectedly) passed 960 times;
{Errno::EPIPE=>38, Errno::ECONNRESET=>2}

… this to be compared with typical output when attempting to write
6th times in a row in the closed pipe :

(Unexpectedly) passed 4 times;
{Errno::EPIPE=>987, Errno::ECONNRESET=>9}

Strangely enough, I don’t seem to get any Errno::ECONNABORTED ever.

I guess it’s a combination of timing and buffering issue.

I’ve reached the same conclusion – which brings me to the question:
what can I do to make the combination of timing and buffering
predictible? Or how may I get around it?

– Emmanuel.