Python for循环减慢并最终挂起

时间:2011-04-20 22:21:16

标签: python for-loop hang

我是Python的新手(截至半小时前)并尝试编写一个简单的脚本来枚举SMTP服务器上的用户。

用户文件是一个简单的列表(每行一个)用户名。

脚本运行正常,但循环的每次迭代都会减慢,直到第14循环,它似乎完全挂起。没有错误 - 我必须^ c。

有人可以解决这个问题吗?

TIA, 汤姆

#!/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 + '\r\n')
        result=s.recv(1024)
        print result

# Close the socket
s.close()

4 个答案:

答案 0 :(得分:8)

您的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秒的延迟。如果您对相关的smtp服务器具有root访问权限,请尝试使用strace来查看服务器是否正在发出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 pat\r\n", 15)       = 15
poll([{fd=9, events=POLLIN}], 1, 300000) = 1 ([{fd=9, revents=POLLIN}])
read(9, "VRFY pat\r\n", 4096)           = 10

答案 1 :(得分:3)

s.recv阻止所以如果你在套接字上没有更多的数据,它将永远阻止。
您必须跟踪您收到的数据量。您需要提前知道这一点,以便客户端和服务器就大小达成一致。

答案 2 :(得分:2)

解决完全相同的问题我也遇到了这个问题。 我几乎可以肯定@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:包括电池。

答案 3 :(得分:1)

一目了然,您的代码没有错误。但是,您应该注意到TCP不是面向“消息”的协议。因此,您不能在循环中使用socket.send,假设在每次调用时实际上都会通过介质发送一条消息。因此,如果某些调用开始在输出缓冲区中缓冲,并且您只是在它之后调用socket.recv,那么您的程序将陷入僵局。

您应该做的是线程或异步代码。也许Twisted Framework可以帮助你。