SMTPServer с asyncore оставляет сокеты открытыми?

Я реализовал базовый SMTPServer, который отлично работает, но я столкнулся с проблемой, и я не знаю, как ее исправить.

При проверке файловых дескрипторов, открытых для $PID процесса, выполняющего мой скрипт (ls -l /proc/$PID/fd/ | grep "socket:" | wc -l), количество файловых дескрипторов продолжает увеличиваться.

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

Я протестировал локально некоторые скрипты Python, подключающиеся к этому коду SMTPServer, и даже если я не закрываю соединение, когда клиентский процесс останавливается, клиент закрывает соединение и дескриптор файла удаляется.

Я думал о реализации тайм-аута для ожидания или перезапуска процесса каждые N подключений, но это просто плохие исправления для более серьезной проблемы.

Вы знаете, откуда это взялось бы?

Для информации, моя оболочка process_message окружена

try:
    # ...
except:
    return '450 Please try again later"

Поэтому исключение из моего кода должно быть хорошо обработано.

Вот мой код (упрощенный):

class MyMXServer(SMTPServer):
    def __init__(self, host='127.0.0.1', port=25):
        self.srs = SRS.new(os.getenv('SRS_KEY', 'some passwd'))
        return SMTPServer.__init__(self, (host, port), None)

    def process_message(self, greeting, peer, mailfrom, recipients, data, **kwargs):
        try:
            return self._process_message(greeting, peer, mailfrom, recipients, data, **kwargs)
        except:
            logging.getLogger('err').exception('[EXCEPTION]')
            return '450 Please try again later.'

    def _process_message(self, greeting, peer, mailfrom, recipients, data, **kwargs):
        # Various things like checking DNSBL, SPF, Spam, etc

person Cyril N.    schedule 18.01.2018    source источник
comment
У вас есть контроль над клиентами или вы принимаете соединения со всего большого плохого Интернета? Некоторые клиенты могут оставлять соединение зависшим только потому, что они неаккуратны, или того хуже.   -  person tripleee    schedule 18.01.2018
comment
Из других новостей: после окончания обычного расписания будет семинар по использованию grep -c вместо grep | wc -l.   -  person tripleee    schedule 18.01.2018


Ответы (1)


Трудно ответить, не видя вашего кода. Однако вы уже реализовали начальные компоненты обработки исключений. else может и не быть необходимым, но похоже, что пункт finally является обязательным в этой ситуации. Завершите блок, чтобы убедиться, что сокет закрыт:

try:
    # unreliable process
except:
    return '450 Please try again later"
else:
    # successful outcome
finallY:
    socket.close()
person mobiusxs    schedule 18.01.2018
comment
Будет ли, наконец, вызываться только в том случае, если было вызвано за исключением? Или независимо от исхода предыдущих пунктов? - person Cyril N.; 18.01.2018
comment
Это хорошо задокументировано в ссылке. Он будет вызван независимо. - person tripleee; 18.01.2018
comment
finally всегда будет вызываться независимо от того, успешно или нет выполняется try. - person mobiusxs; 18.01.2018
comment
Это не сработает (только что проверил с кодом), так как метод может вернуть «250», если все в порядке, и будет ждать следующей команды. Закрытие сокета вызовет проблемы. - person Cyril N.; 18.01.2018
comment
Если моя гипотеза о том, что некоторые клиенты не закрывают соединение должным образом, верна, проблема может возникнуть до запуска моего кода (например, сразу после команды HELO). Вот почему я не вставил свой код. - person Cyril N.; 18.01.2018
comment
Асинхронность усложняет ситуацию, вам нужно выяснить, где асинхронный сервер поддерживает соединение открытым, но это практически невозможно, не видя больше вашего кода. Возможно, вам нужно обернуть свой код в объект, который имеет явный деструктор, который заботится об очистке. - person tripleee; 18.01.2018
comment
@КирилН. - Это цель пункта else. Если try успешно, перейдите к else. После завершения else перейдите к finally. Если try не удается, перейдите к except, затем перейдите к finally. Это именно тот процесс, который вам нужен, чтобы правильно закрыть сокет. Вы не можете полагаться на то, что клиент действует определенным образом. - person mobiusxs; 18.01.2018
comment
Я добавил часть своего кода, которая обрабатывает ответ, но опять же, я не вижу, что там может дать сбой. Что касается идеи обернуть его в класс для обработки уничтожения, если моя гипотеза верна, это не сработает (поскольку процесс зависает) - person Cyril N.; 18.01.2018
comment
Я, наконец, думаю, кто виноват (tldr: я). Я был настолько сосредоточен на проблеме с сокетом, что был уверен, что это связано с Smtpd. Но я забыл, что использовал другой экземпляр сокета в другом месте кода, и знаете что? Этот сокет иногда вызывал исключения, в результате чего сокет не закрывался... Я исправил это. Я не уверен, что это именно он, но все указывает на это! Я посмотрю, продолжает ли FD увеличиваться. Спасибо за вашу помощь ! - person Cyril N.; 18.01.2018