Python для цикла замедляется и даже зависает

#python #for-loop #freeze

#python #для цикла #замораживание

Вопрос:

Я совершенно новичок в Python (по состоянию на полчаса назад) и пытаюсь написать простой скрипт для перечисления пользователей на SMTP-сервере.

Файл users представляет собой простой список (по одному на строку) имен пользователей.

Скрипт работает нормально, но с каждой итерацией цикла он замедляется до тех пор, пока, примерно в 14 цикле, кажется, что он полностью зависает. Ошибки нет — я должен ^ c.

Кто-нибудь может пролить свет на проблему, пожалуйста?

ТИА, Том

 #!/usr/bin/python

import socket
import sys

if len(sys.argv) != 2:
        print "Usage: vrfy.py <username file>"
        sys.exit(0)

#open user file
file=open(sys.argv[1], 'r')
users=[x.strip() for x in file.readlines()]
file.close

#Just for debugging
print users

# Create a Socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to the Server
connect=s.connect(('192.168.13.222',25))

for x in users:
        # VRFY a user
        s.send('VRFY '   x   'rn')
        result=s.recv(1024)
        print result

# Close the socket
s.close()
  

Комментарии:

1. Довольно продвинутый материал для новичка. 🙂

2. Однако два замечания: file.close ничего не делает, как вы хотите file.close() (и в будущем вам следует постепенно привыкать к контекстным менеджерам), и .readlines() вызов бессмыслен (вы можете просто перебирать файл построчно, как есть).

3. В чем проблема? Ты ВЫВОДИШЬ ИЗ СЕБЯ СИСТЕМНОГО администратора.

4. В случае, если комментарий Игнасио был непонятен, вполне вероятно, что системный администратор намеренно замедляет ваше соединение, потому что они думают, что вы спам-бот. Посмотрите, замедлится ли это, если вы сделаете это на другом языке.

Ответ №1:

Скорее всего, ваш SMTP-сервер прерывает ваше клиентское соединение. Это защита от неуправляемых клиентов или клиентов, которые отправляют большие объемы «нежелательных» команд. Со страницы руководства для Postfix smtpd:

    smtpd_junk_command_limit (normal: 100, stress: 1)
          The number of junk commands (NOOP, VRFY, ETRN or  RSET)  that  a
          remote  SMTP  client  can  send  before  the Postfix SMTP server
          starts to increment the error counter with each junk command.
  

Демон smtpd вставит 1-секундную задержку перед ответом после того, как будет замечено определенное количество мусора. Если у вас есть root-доступ к соответствующему smtp-серверу, попробуйте выполнить попытку, чтобы узнать, выдаются ли сервером системные вызовы nanosleep.

Вот результат запуска вашего скрипта на моем локальном сервере. После 100 команд VRFY он переходит в режим ожидания между командами. Ваш сервер может иметь нижний предел в ~ 15 нежелательных команд:

 nanosleep({1, 0}, 0x7fffda9a67a0)       = 0
poll([{fd=9, events=POLLOUT}], 1, 300000) = 1 ([{fd=9, revents=POLLOUT}])
write(9, "252 2.0.0 patrn", 15)       = 15
poll([{fd=9, events=POLLIN}], 1, 300000) = 1 ([{fd=9, revents=POLLIN}])
read(9, "VRFY patrn", 4096)           = 10
  

Комментарии:

1. Я этого не делаю (хотя у меня есть разрешение тыкать в это :)). Интересная мысль, хотя, спасибо

2. Пожалуйста. Легко подтвердить, подключившись по телефону к порту smtpd и вручную выполнив команды «VRFY user». Если вы заметили, что ответы задерживаются, это верный признак того, что ваш сервер работает с задержкой. Удачи!

3. Подтверждено вручную, как вы предложили. Точно такое же поведение. Похоже, проблема связана с конфигурацией SMTP-сервера, а не со сценарием. Очень хорошо замечено и спасибо!

Ответ №2:

s.recv блокирует, поэтому, если у вас больше нет данных в сокете, он будет заблокирован навсегда.
Вы должны отслеживать, сколько данных вы получаете. Вам нужно знать это заранее, чтобы клиент и сервер могли договориться о размере.

Комментарии:

1. @eat_a_lemon — Спасибо за ваш ответ. Я не понимаю, почему кажется, что это постепенно замедляется и останавливается… Основываясь на вашем ответе, я бы ожидал, что сокет заблокируется на первой итерации, а не будет постепенно замедляться…

2. @Tom ну, это зависит от скорости, с которой вы получаете данные с сервера.

3. Боюсь, у меня пока нет репутации для этого… Будет сделано, если / когда я это сделаю, хотя 🙂

Ответ №3:

Решая точно такую же проблему, я также столкнулся с проблемой. Я почти уверен, что @samplebias прав. Я обнаружил, что могу обойти «блокировку», еще больше злоупотребляя плохой системой, разрывая и восстанавливая каждое соединение:

 #[ ...Snip... ]
import smtplib
#[ ...Snip... ]
for USER in open(opts.USERS,'r'):
    smtpserver = smtplib.SMTP(HOST,PORT)
    smtpserver.ehlo()
    verifyuser = smtpserver.verify(USER)
    print("%s %s:  %s") % (HOST.rstrip(), USER.rstrip(), verifyuser)
    smtpserver.quit()
  

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

PS, python: батарейки в комплекте.

Ответ №4:

На первый взгляд, в вашем коде нет ошибок. Однако вы должны заметить, что TCP не является протоколом, ориентированным на «сообщения». Итак, вы не можете использовать сокет.отправка в цикле предполагает, что одно сообщение будет фактически отправлено через среду при каждом вызове. Таким образом, если некоторые вызовы начинают буферизоваться в выходном буфере, и вы просто вызываете socket.recv после этого ваша программа застрянет в тупике.

Что вам следует сделать, так это многопоточный или асинхронный код. Возможно, Twisted Framework может вам помочь.

Комментарии:

1. Спасибо, я посмотрю на это.

2. Это не совсем правильно, взаимоблокировки не будет. Безусловно, при вызове есть проблемы .recv(1024) (например, ответ короче или длиннее ожидаемого), но взаимоблокировка не будет одной из них.

3. сокет. у recv нет никаких проблем при чтении меньшего количества данных, чем присутствует в буфере, остаток все еще будет в буфере и может быть прочитан через некоторое время после первого вызова recv. Возможно, OP может провести эксперимент: отправить все запросы в цикле и после этого прочитать входной буфер. Или, чтобы не потерять какие-либо данные, реализуйте программу с использованием потоков. Логично, пришлите нам результат после.