So borrowing roughly from the standard library socket.create_connection
function, which does connection to multiple address/port pairs for each IP resolved for any given hostname, with the connection done using blocking sockets following the sequence of IP addresses as returned by DNS. Adapting that to accept multiple raw IP addresses and make use of non-blocking sockets can be done roughly with the following function:
import socket
import errno
def make_socket_from_addresses(addresses, port, *args, **kwargs):
sockets = {} # mapping of the actively checked sockets
dests = [] # the list of all destination pairs
for address in addresses:
dest = (address, port)
sock = socket.socket(*args, **kwargs)
sock.setblocking(0)
sockets[dest] = sock
dests.append(dest)
result = None
while sockets and result is None:
for dest in dests:
sock = sockets.get(dest)
if not sock:
continue
code = sock.connect_ex(dest)
if code == 0:
# success, track the result.
result = sock
sockets.pop(dest)
break
elif code not in (errno.EALREADY, errno.EINPROGRESS):
# assume any not already/in-progress are invalid/dead and so
# prune them.
sockets.pop(dest)
# should insert some very minute timeout here
for _, sock in sockets.items():
# close any remaining sockets
sock.close()
return result
To make use of this function, a list of addresses to check and a port must be supplied, along with the relevant arguments to the socket.socket
constructor. For example:
# various google.com addresses
addresses = ['216.58.203.110', '172.217.25.46', '172.217.6.78']
port = 80
sock = make_socket_from_addresses(
addresses, port, socket.AF_INET, socket.SOCK_STREAM)
print(sock)
Running that:
<socket.socket fd=3, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('10.0.0.1', 59454), raddr=('172.217.25.46', 80)>
Usage of select
may be beneficial, the example function provided serves only as an illustration on how the non-blocking loop might look like and how it should work.