Author trent
Recipients trent
Date 2008-04-06.21:20:22
SpamBayes Score 0.0467266
Marked as misclassified No
Message-id <1207516827.65.0.719453068499.issue2550@psf.upfronthosting.co.za>
In-reply-to
Content
[Updating the issue with relevant mailing list conversation]
Interesting results!  I committed the patch to test_socket.py in 
r62152.  I was expecting all other platforms except for Windows to 
behave consistently (i.e. pass).  That is, given the following:

        import socket
        host = '127.0.0.1'
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind((host, 0))
        port = sock.getsockname()[1]
        sock.close()
        del sock

        sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock1.bind((host, port))
        sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock2.bind((host, port))
        ^^^^

....the second bind should fail with EADDRINUSE, at least according to 
the 'SO_REUSEADDR and SO_REUSEPORT Socket Options' section in chapter 
7.5 of Stevens' UNIX Network Programming Volume 1 (2nd Ed):

"With TCP, we are never able to start multiple servers that bind
 the same IP address and same port: a completely duplicate binding.
 That is, we cannot start one server that binds 198.69.10.2 port 80
 and start another that also binds 198.69.10.2 port 80, even if we
 set the SO_REUSEADDR socket option for the second server."

The results: both Windows *and* Linux fail the patched test; none of 
the buildbots for either platform encountered an EADDRINUSE 
socket.error after the second bind().  FreeBSD, OS X, Solaris and Tru64 
pass the test -- EADDRINUSE is raised on the second bind.  (Interesting 
that all the ones that passed have a BSD lineage.)

I've just reverted the test in r62156 as planned.  The real issue now 
is that there are tests that are calling test_support.bind_socket() 
with the assumption that the port returned by this method is 'unbound', 
when in fact, the current implementation can't guarantee this:

def bind_port(sock, host='', preferred_port=54321):
    for port in [preferred_port, 9907, 10243, 32999, 0]:
        try:
            sock.bind((host, port))
            if port == 0:
                port = sock.getsockname()[1]
            return port
        except socket.error, (err, msg):
            if err != errno.EADDRINUSE:
                raise
            print >>sys.__stderr__, \
                '  WARNING: failed to listen on port %d, trying 
another' % port

This logic is only correct for platforms other than Windows and Linux.  
I haven't looked into all the networking test cases that rely on 
bind_port(), but I would think an implementation such as this would be 
much more reliable than what we've got for returning an unused port:

def bind_port(sock, host='127.0.0.1', *args):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host, 0))
    port = s.getsockname()[1]
    s.close()
    del s

    sock.bind((host, port))
    return port

Actually, FWIW, I just ran a full regrtest.py against trunk on Win32 
with this change in place and all the tests still pass.

Thoughts?

    Trent.
History
Date User Action Args
2008-04-06 21:20:28trentsetspambayes_score: 0.0467266 -> 0.0467266
recipients: + trent
2008-04-06 21:20:27trentsetspambayes_score: 0.0467266 -> 0.0467266
messageid: <1207516827.65.0.719453068499.issue2550@psf.upfronthosting.co.za>
2008-04-06 21:20:26trentlinkissue2550 messages
2008-04-06 21:20:23trentcreate