Message65050
[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. |
|
Date |
User |
Action |
Args |
2008-04-06 21:20:28 | trent | set | spambayes_score: 0.0467266 -> 0.046726618 recipients:
+ trent |
2008-04-06 21:20:27 | trent | set | spambayes_score: 0.0467266 -> 0.0467266 messageid: <1207516827.65.0.719453068499.issue2550@psf.upfronthosting.co.za> |
2008-04-06 21:20:26 | trent | link | issue2550 messages |
2008-04-06 21:20:23 | trent | create | |
|