Попытайтесь подключиться к нескольким IP-адресам и получите первый успешный - PullRequest
0 голосов
/ 13 февраля 2019

Есть ли способ с помощью socket попытаться подключиться к нескольким 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), но примененное во время установления соединения (установка блокировки относится кsend() и recv()).И что-то похожее на select.select, но применимо к установлению соединения (вместо запуска при вводе-выводе).

1 Ответ

0 голосов
/ 13 февраля 2019

Таким образом, примерно заимствуя из стандартной библиотеки 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 может быть полезным, приведенная в качестве примера функция служит только в качестве иллюстрации того, как неблокирующий цикл можеткак выглядит и как должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...