2009-07-30 3 views
9

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

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

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

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

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

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

Нужно ли мне использовать потоки?

ответ

4

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

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

В качестве примера, следующие работы на все мои системах 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-адреса в вашем цикле async

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

+0

Хорошо, я должен извиниться здесь. Я взял код прямо из документов Python, так что это был не мой код, я принял это как должное, это правильно. И это не сработало. Мне часто случалось, что люди давали мне совет, который они даже не проверяли. Я никогда не мог догадаться, что моя ОС будет проблемой, а не кодом, поэтому я подумал, что вы только один парень, думающий, что умный и скопируйте мне код документации, даже не проверяя, работает ли он. Извините еще раз за это. Сегодня я выбросил 3 полные версии, потратив 6 часов, чтобы обнаружить, что проблема MacOS. – Tom

+0

btw Я тестировал это снова вместе с другом в своей linux-блоке, и даже getAddrInfo, похоже, не блокирует его. Мы получаем сообщение об ошибке: [Errno 115] Выполняется операция. Таким образом, теоретически даже асинхронная работа с невосприимчивыми хостами может работать в Linux. – Tom

+0

@Tom - np, я согласен, здесь есть тонны неосведомленных ответов, особенно в не-окнах. Что еще хуже, команды неосведомленных в конечном итоге завышают друг друга, затрудняя получение правильных ответов. – JimB

0

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

+0

Я использую это, и он все еще блокируется при подключении. – Tom

8

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

In C, coding select is fairly complex. In Python, it's a piece of cake, but it's close enough to the C version that if you understand select in Python, you'll have little trouble with it in C.

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

You pass select three lists: the first contains all sockets that you might want to try reading; the second all the sockets you might want to try writing to, and the last (normally left empty) those that you want to check for errors. You should note that a socket can go into more than one list. The select call is blocking, but you can give it a timeout. This is generally a sensible thing to do - give it a nice long timeout (say a minute) unless you have good reason to do otherwise.

In return, you will get three lists. They have the sockets that are actually readable, writeable and in error. Each of these lists is a subset (possibly empty) of the corresponding list you passed in. And if you put a socket in more than one input list, it will only be (at most) in one output list.

If a socket is in the output readable list, you can be as-close-to-certain-as-we-ever-get-in-this-business that a recv on that socket will return something. Same idea for the writeable list. You'll be able to send something. Maybe not all you want to, but something is better than nothing. (Actually, any reasonably healthy socket will return as writeable - it just means outbound network buffer space is available.)

If you have a "server" socket, put it in the potential_readers list. If it comes out in the readable list, your accept will (almost certainly) work. If you have created a new socket to connect to someone else, put it in the potential_writers list. If it shows up in the writeable list, you have a decent chance that it has connected.

+0

Он конкретно говорит, что он заблокирован при подключении(). Выберите только то, что доступно для чтения или записи. – JimB

+1

См. Последний параграф моего ответа. При мультиплексировании 'select' вам не нужно ждать 1000 секунд, прежде чем выполнять полезную работу. С небольшим тайм-аутом вы все равно можете делать полезную работу, если все конечные точки не подключены, и только короткое ожидание. Скрученный, конечно, альтернатива, но, как вы сказали себе, «это немного тяжело попасть». –

+0

Ahh, я вижу проблему ... он установил тайм-аут, что означает, что сокет * имеет * блокировку. – JimB

4

Использовать twisted.

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

+2

Скрученный приносит такое счастье. Я работаю с ним каждый день и пытаюсь убедить тех, кто борется с параллелизмом, что он сделает их жизнь намного проще. Конечно, мои коллеги, по крайней мере, видят разницу. – Dustin

+1

Я использовал скрученный ранее, его довольно хороший, но документация тоже скручена. Также будет сложно интегрировать мой источник в это. Вы уверены, что он не блокируется при подключении? Мог бы попытаться это сделать. – Tom

6

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

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

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

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

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

+0

, который в значительной степени сокращает суть проблемы. Кажется, у меня проблемы с DNS. Поведение моего приложения (по крайней мере, на начальном этапе) довольно похоже на portscanner: я зависим от очень быстрых результатов, работает ли соединение или нет.Использование getaddrinfo для несуществующих блоков имен узлов также для неблокирующих сокетов, что плохо (для меня). Я мог бы также подключиться к множеству несуществующих хостов, и я не могу позволить себе ждать 10 секунд на каждом несуществующем хосте. – Tom

+1

Моя цель была совсем другая, но была исправлена ​​путем изменения порядка. т.е. сначала подключить, затем установить блокировку. – Ben

+0

@Ben, который исправил мою проблему тоже! Благодаря! – JeromeJ

Смежные вопросы