-
-
Save gabrielfalcao/20e567e188f588b65ba2 to your computer and use it in GitHub Desktop.
# Getting a random free tcp port in python using sockets | |
def get_free_tcp_port(): | |
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
tcp.bind(('', 0)) | |
addr, port = tcp.getsockname() | |
tcp.close() | |
return port | |
def get_free_tcp_address(): | |
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
tcp.bind(('', 0)) | |
host, port = tcp.getsockname() | |
tcp.close() | |
return 'tcp://{host}:{port}'.format(**locals()) |
Might be helpful for others: the port from get_free_tcp_port
cannot be used immediately after function returns. port will be in state TIME_WAIT
for approx 30-60s after closing and cannot be reused before that without explicitly passing SO_REUSEPORT
This is useful for showing that
0
as the socket number lets the OS select the portgetsocketname()
will return the address.
But beyond that you should never do it this way. As @corydodt points out there's a race condition. Rather like creating a temporary file you should both allocate the address (including port) and create the socket in one atomic step:
def open_ephemeral_socket():
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp.bind(('', 0))
return tcp.getsockname(), address
In this case you would then use the returned socket not try to open a new one on the same port.
Note that the atomic nature of this code is backed up by the operating system. It allocates the port and creates the socket in one go so there is no chance of any race condition.
more modern and pythonic:
import socket
from contextlib import closing
def get_local_ip() -> str:
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.connect(("8.8.8.8", 53))
return s.getsockname()[0]
This has a race condition, since another process may grab that port after tcp.close(). This might be ok, if you simply catch the exception and try again, although it strikes me as possibly insecure to do so.