New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
loop.create_server
with port=0 uses different ports for ipv4 & ipv6
#89856
Comments
To create a new server with Running the example test code (attached) results in:
This behavior can be worked around by manually handling |
Is there an OS interface to ensure the same port on both stacks? I don't know of one (although of course one might exist), in which case I don't know how you'd ensure they're both the same. |
I don't think this is needed? Right now the code processes as:
In this case, each call to What I'm asking for is that if the user passes in |
What do you do if a port is bound for IPv4, but is in use for IPv6? |
Hmmm, I'd find that situation a bit surprising, but I suppose it could happen. Looks like tornado just errors, and that seems to work fine for them in practice (https://github.com/tornadoweb/tornado/blob/790715ae0f0a30b9ee830bfee75bb7fa4c4ec2f6/tornado/netutil.py#L153-L182). Binding IPv4 first might help reduce the chance of a collision, since I suspect there are more IPv4-only applications than IPv6-only. |
Tornado solution sounds weak; it can fail the server start if free ports are available. I concur with Eric, I'm not aware of an OS API call that binds both IPv4 and IPv6 to the same random port. |
Sure, but By default Is there a use case where the current behavior (binding to multiple random ports) is desired? |
If you decline that a change is needed here, at the very least the current behavior of |
PR for documentation fix is appreciated. Random fails to bind the same port if free ports are available is kind of regression. Is tornado the only example or you are aware of other libraries with such behavior? |
A quick survey of other language network stacks didn't turn anything up, But I also didn't find any implementations (other than asyncio & tornado) that bind multiple sockets with a single api call (as I think part of the issue here is that dual IPV6 & IPV4 support is intentionally disabled in asyncio (and tornado), so two sockets are needed (one to support each interface). Other TCP implementations (e.g. both go and rust) don't disable this, so one listener == one socket. This makes comparing API designs across stacks harder - with e.g. Go it's straightforward to listen on a random port on IPV4 & IPV6 with a single TCPListener, since both can be handled by a single socket. Since this is disabled (by default) in asyncio we end up using 2 sockets and run into the issue described above. Also note that this issue will trigger for any address that resolves to multiple interfaces (not just # Start a server on localhost with a random port
server = await loop.create_server(
EchoServerProtocol,
host="localhost",
port=0
)
# Retrieve and log the port
port = server.sockets[0].getsockname()[1]
print(f"listening at tcp://localhost:{port}") As written, this looks correct enough, but on systems where localhost resolves to multiple interfaces this will accidentally listen on multiple ports (instead of one). This can be fixed with some additional logic external to asyncio, but it makes for a much less straightforward asyncio example. |
Apologies for the delay here. I've pushed a documentation patch at #29760. |
Thanks for the PR, @jcristharif. |
port
parameter toloop.create_server
#29760port
parameter toloop.create_server
(GH-29760) #29762port
parameter toloop.create_server
(GH-29760) #29763port
parameter toloop.create_server
(GH-29760) #29764Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: