尝试连接到多个IP地址并获得第一个成功的IP地址

时间:2019-02-13 05:41:42

标签: python sockets

是否可以使用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上触发)。

1 个答案:

答案 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可能是有益的,所提供的示例函数仅用作说明非阻塞循环的外观以及其工作方式。

相关问题