是否可以使用socket
尝试连接到多个IP地址并获得第一个成功的IP地址?
一个简单的概念是这样的:
targets = ['1.2.3.4', '2.3.4.5', '3.4.5.6']
try:
s = socket_try_connect_one(targets, port=80, timeout=300)
# at this point, s contains the socket connection to one of the targets
except TimeoutError:
print('Cannot connect to any servers!')
sys.exit(1)
我应该如何实现socket_try_connect_one
才能获得预期的行为?在标准库中是否有内置方法可以做到这一点?
编辑后添加:
我避免使用多线程/多处理方法,因为对于这种简单的需求,可能可能会过大。
相反,我正在寻找与.setblocking(0)
类似的东西,但在连接建立期间应用了 (setblocking适用于send()
和{{ 1}})。类似于recv()
,但适用于连接建立(而不是在I / O上触发)。
答案 0 :(得分:2)
因此,大致借鉴了标准库socket.create_connection
函数的功能,该函数针对为任何给定主机名解析的每个IP都与多个地址/端口对建立连接,并使用阻塞套接字按照返回的IP地址顺序进行连接通过DNS。可以使用以下功能粗略地对其进行修改以接受多个原始IP地址并使用无阻塞套接字:
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
要使用此功能,必须提供要检查的地址列表和端口,以及socket.socket
构造函数的相关参数。例如:
# 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)
运行:
<socket.socket fd=3, family=AddressFamily.AF_INET, type=2049, proto=0, laddr=('10.0.0.1', 59454), raddr=('172.217.25.46', 80)>
使用select
可能是有益的,所提供的示例函数仅用作说明非阻塞循环的外观以及其工作方式。