Как я могу получить неблокирующие сокеты connect ()? - PullRequest
10 голосов
/ 30 июля 2009

У меня здесь довольно простая проблема. Мне нужно общаться с большим количеством хостов одновременно, но мне не нужна синхронизация, потому что каждый запрос самодостаточен.

Из-за этого я решил работать с асинхронными сокетами, а не со спам-потоками. Теперь у меня есть небольшая проблема:

Асинхронная работа работает как чудо, но когда я подключаюсь к 100 хостам и получаю 100 тайм-аутов (тайм-аут = 10 секунд), то я жду 1000 секунд, просто чтобы выяснить, все ли мои соединения не удалось.

Есть ли способ также получить неблокирующие соединения сокетов? Мой сокет уже настроен на неблокирование, но вызовы connect () все еще блокируются.

Сокращение времени ожидания не является приемлемым решением.

Я делаю это в Python, но, полагаю, язык программирования в данном случае не имеет значения.

Мне действительно нужно использовать темы?

Ответы [ 5 ]

8 голосов
/ 30 июля 2009

Используйте модуль select. Это позволяет вам ожидать завершения ввода / вывода на нескольких неблокирующих сокетах. Вот дополнительная информация о выборе. Из ссылки на страницу:

В C кодирование select довольно сложно. В Python это кусок пирога, но это достаточно близко к версии C что если вы понимаете, выберите в Python, у тебя будет мало проблем с этим в C.

ready_to_read, ready_to_write, in_error = select.select(
                  potential_readers, 
                  potential_writers, 
                  potential_errs, 
                  timeout)

Вы передаете select три списка: первый содержит все сокеты, которые вы могли бы хочу попробовать читать; второе все розетки, которые вы можете попробовать пишу, и последнее (обычно оставлено пустым) те, которые вы хотите проверить на ошибки. Вы должны отметить, что сокет может входить в несколько список. Вызов select блокируется, но Вы можете дать ему тайм-аут. Это как правило, разумная вещь, чтобы сделать - дать ему хороший длительный перерыв (скажем, минут), если у вас нет веских причин поступай иначе.

В ответ вы получите три списка. У них есть розетки, которые на самом деле читаемый, записываемый и в ошибка. Каждый из этих списков является подмножеством (возможно, пустой) соответствующего список, который вы передали. И если вы положите сокет в более чем одном входном списке, это будет только (максимум) в одном выводе список.

Если сокет находится на выходе для чтения список, вы можете быть а-близко к уверены-как-мы-все получить в этом-бизнеса что recv на этом сокете вернется что-то. Та же идея для записи список. Вы сможете send что-то. Может быть, не все, что вы хотите, но что-то лучше, чем ничего. (На самом деле, любой достаточно здоровый сокет вернется как записываемый - это просто означает исходящий сетевой буфер есть место.)

Если у вас есть «серверный» сокет, поставьте его в списке потенциальных_читателей. Если оно выходит в читабельном списке, ваш принять будет (почти наверняка) работать. Если вы создали новый сокет для подключиться к кому-то еще, положить его в список потенциальных писателей. Если это появляется в списке для записи, у вас есть Приличный шанс, что он соединился.

7 голосов
/ 31 июля 2009

К сожалению, нет примера кода, который показывает ошибку, поэтому немного трудно понять, откуда взялся этот блок.

Он делает что-то вроде:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.connect(("www.nonexistingname.org", 80))

Модуль сокета использует getaddrinfo для внутреннего использования, что является блокирующей операцией, особенно когда имя хоста не существует. Стандартный совместимый DNS-клиент подождет некоторое время, чтобы выяснить, действительно ли имя не существует или задействованы только некоторые медленные DNS-серверы.

Решение состоит в том, чтобы подключаться только к ip-адресам или использовать DNS-клиент, который разрешает неблокирующие запросы, например pydns .

5 голосов
/ 30 июля 2009

Вам также необходимо распараллелить соединения, так как сокеты блокируются, когда вы устанавливаете время ожидания. Кроме того, вы не можете установить время ожидания и использовать модуль выбора.

Это можно сделать с помощью класса диспетчера в модуле asyncore . Взгляните на базовый пример http клиента . Несколько экземпляров этого класса не будут блокировать друг друга при подключении. Вы можете сделать это так же легко, используя потоки, и я думаю, что это упрощает отслеживание времени ожидания сокетов, но, поскольку вы уже используете асинхронные методы, вы также можете остаться на той же дорожке.

Например, следующее работает на всех моих системах Linux

import asyncore, socket

class client(asyncore.dispatcher):
    def __init__(self, host):
        self.host = host
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, 22))

    def handle_connect(self):
        print 'Connected to', self.host

    def handle_close(self):
        self.close()

    def handle_write(self):
        self.send('')

    def handle_read(self):
        print ' ', self.recv(1024)

clients = []
for i in range(50, 100):
    clients.append(client('cluster%d' % i))

asyncore.loop()

Где в cluster50 - cluster100, существует множество машин, которые не отвечают или не существуют. Это немедленно начинает печатать:

Connected to cluster50
  SSH-2.0-OpenSSH_4.3

Connected to cluster51
  SSH-2.0-OpenSSH_4.3

Connected to cluster52
  SSH-2.0-OpenSSH_4.3

Connected to cluster60
  SSH-2.0-OpenSSH_4.3

Connected to cluster61
  SSH-2.0-OpenSSH_4.3

...

Это, однако, не учитывает getaddrinfo, который должен блокироваться. Если у вас возникли проблемы с разрешением DNS-запросов, все должно подождать. Вероятно, вам нужно собрать DNS-запросы отдельно и использовать IP-адреса в асинхронном цикле

Если вы хотите набор инструментов большего размера, чем asyncore, взгляните на Twisted Matrix . Это немного тяжело, но это лучший инструментарий сетевого программирования, который вы можете получить для python.

4 голосов
/ 30 июля 2009

Использование витой .

Это асинхронный сетевой движок, написанный на Python, поддерживающий множество протоколов, и вы можете добавить свой собственный. Может использоваться для разработки клиентов и серверов. Не блокируется при подключении.

0 голосов
/ 30 июля 2009

Вы смотрели на asyncore модуль? Может быть именно то, что вам нужно.

...