asyncore.loop не завершается, когда больше нет подключений

Я следую некоторому примеру кода для использования asyncore здесь, только установив значение timeout для asyncore.loop как в следующем полном примере:

import smtpd
import asyncore

class CustomSMTPServer(smtpd.SMTPServer):

    def process_message(self, peer, mailfrom, rcpttos, data):
        print 'Receiving message from:', peer
        print 'Message addressed from:', mailfrom
        print 'Message addressed to  :', rcpttos
        print 'Message length        :', len(data)
        return

server = CustomSMTPServer(('127.0.0.1', 1025), None)

asyncore.loop(timeout = 1)

Я ожидал, что тайм-аут произойдет через 1 секунду, но это не так. Код выполняется намного дольше, чем одна секунда. Что мне здесь не хватает?


person Alex    schedule 23.01.2013    source источник
comment
@pst: подсчет не вариант. Я не знаю, будет ли заранее ноль или больше нуля подключений.   -  person Alex    schedule 23.01.2013
comment
Тогда перестань ходить вокруг да около   -  person    schedule 23.01.2013
comment
@pst Я не знаю, что ты имеешь в виду. Мой вопрос: почему тайм-аут для asyncore.loop() не завершает функцию asyncore.loop() по истечении указанного времени.   -  person Alex    schedule 23.01.2013
comment
Ну, рассмотрим некоторые случаи: 1) в документации врет, что он перестанет работать, когда больше не будет открытых каналов, 2) в документации врет о том, что делает timeout, 3) asyncore не использует указанный тайм-аут с select 4) есть есть открытые каналы .. Я подозреваю, что это скоро закончится, если будет указано количество (скажем, 2). Если это действительно так, то № 2 и № 3, вероятно, можно исключить.   -  person    schedule 23.01.2013
comment
@pst: я не понимаю, что вы имеете в виду. Может быть, вы могли бы начать отвечать на следующий вопрос ясным, кратким и хорошо сформулированным ответом: Какова цель аргумента timeout в asyncore.loop()?   -  person Alex    schedule 23.01.2013
comment
В документации сказано, что он делает. Поскольку вы делаете утверждение, я предложил некоторую гипотезу, которой можно следовать, чтобы подтвердить или опровергнуть это утверждение.   -  person    schedule 23.01.2013
comment
давайте продолжим это обсуждение в чате   -  person Alex    schedule 23.01.2013


Ответы (3)


Аргумент timeout для asyncore.loop() — это количество времени, в течение которого вызов select.select будет ожидать данных. Если нет данных до того, как timeout закончится, он зациклится и снова вызовет select.select.

То же самое и с идеей channels. Это означает не открытые сокеты, а активные экземпляры asyncore.dispatcher или asynchat.async_chat. Если вы хотите остановить цикл, вам придется вызвать метод close() для ВСЕХ зарегистрированных экземпляров.

В вашем случае server.close() закроет экземпляр/канал и удалит его из цикла asyncore. Если больше нет активных каналов, этот цикл завершится.

person Wessie    schedule 23.01.2013
comment
Как я могу вызвать метод close, если asyncore.loop() блокирует мой код? - person Alex; 23.01.2013
comment
@Alex Вы должны либо запустить asyncore.loop() в новом потоке, либо вызвать close внутри одного из handlers. Я не знаю, как вы определяете, когда выходить, поэтому, если вы сообщите нам, как вы определяете, мы могли бы помочь больше. - person Wessie; 23.01.2013
comment
Я пытался запустить asyncore.loop() в потоке, см. stackoverflow.com/questions/14483195/, но мне нужно выяснить, где и как вызывать close или как и где изменить любой hander метод. Если вы знаете, как, я был бы очень признателен, если бы вы могли уточнить это в другом вопросе. - person Alex; 23.01.2013
comment
@Alex Алекс Я опубликовал ответ на связанный вопрос. Пожалуйста, прокомментируйте там, если вам нужна дополнительная помощь. - person Wessie; 23.01.2013

Я действительно не знаю, действительно ли аргумент timeout для asyncore.loop() предназначен для тайм-аута вызова функции asyncore.loop() после указанного времени, но вот квитанция, чтобы сделать эту функцию тайм-аутом после указанного времени (заменив строку на asyncore.loop() в примере кода) :

import signal

class TimeoutError(Exception): pass

# define the timeout handler
def handler(signum, frame):
    raise TimeoutError()

# set the timeout handler and the signal duration
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
try:
    asyncore.loop()
except TimeoutError as exc:
    print "timeout"
finally:
    signal.alarm(0)
person Alex    schedule 23.01.2013
comment
Примечание: недоступно в Windows с Python 2.7. - person Josh Petitt; 31.01.2014

Тайм-аут asyncore.loop() — это тайм-аут для select().

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

while True:
    do_something()
    select(...)
    do_something_else()

Если я выполняю симуляцию с сокетами, защищенными брандмауэром, в моем Python 2.7.3 тайм-аут asyncore.loop() через 1 минуту после того, как данные не будут получены из какого-либо сокета.

Я нашел очень полезным иметь следующий метод в «подклассе» asyncore.dispatcher:

def handle_error(self):
    raise

Таким образом, у меня был «правильный» дамп исключений.

Поскольку я не хотел иметь исключение, позже я изменил его на что-то вроде:

def handle_error(self):
    print "Error downloading %s" % self.host
    pass

Теперь мой код работает корректно, без исключений.

Я не нашел способ контролировать время ожидания.

person Nick    schedule 03.04.2013