我正在尝试用Python实现一个简单的portscanner。它的工作原理是创建许多工作线程,这些线程扫描队列中提供的端口。他们将结果保存在另一个队列中。扫描所有端口后,线程和应用程序应终止。问题在于:对于少量端口,一切正常,但如果我尝试扫描200个或更多端口,应用程序将陷入僵局。我不知道,为什么。
class ConnectScan(threading.Thread):
def __init__(self, to_scan, scanned):
threading.Thread.__init__(self)
self.to_scan = to_scan
self.scanned = scanned
def run(self):
while True:
try:
host, port = self.to_scan.get()
except Queue.Empty:
break
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((host, port))
s.close()
self.scanned.put((host, port, 'open'))
except socket.error:
self.scanned.put((host, port, 'closed'))
self.to_scan.task_done()
class ConnectScanner(object):
def scan(self, host, port_from, port_to):
to_scan = Queue.Queue()
scanned = Queue.Queue()
for port in range(port_from, port_to + 1):
to_scan.put((host, port))
for i in range(20):
ConnectScan(to_scan, scanned).start()
to_scan.join()
有人看到可能出错的地方吗?另外,我要感谢一些tipp如何在Python中调试这样的线程问题。
答案 0 :(得分:2)
我没有看到你的代码有任何明显的错误,但是因为它永远不会被击中 - self.to_scan.get()
将永远等待,而不是提升Queue.Empty。鉴于您在启动线程之前加载了要扫描的端口的队列,您可以将其更改为self.to_scan.get(False)
,以便在声明所有端口后正确退出工作线程。
结合使用非守护程序线程(在主线程完成后将使进程保持活动状态的线程)的事实,这可能是挂起的原因。尝试在to_scan.join()
之后打印一些内容,看看它是在那里停止,还是在流程退出时停止。
正如Ray所说,如果在self.to_scan.get()
和self.to_scan.task_done()
之间引发了除socket.error之外的异常,则join
调用将挂起。它可以帮助改变代码以使用try / finally确保:
def run(self):
while True:
try:
host, port = self.to_scan.get(False)
except Queue.Empty:
break
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((host, port))
s.close()
self.scanned.put((host, port, 'open'))
except socket.error:
self.scanned.put((host, port, 'closed'))
finally:
self.to_scan.task_done()
通常,调试多线程进程很棘手。我试图避免任何无限期的阻塞 - 最好让一些事情吵闹,因为超时太短而不是让它永远停止等待永远不会出现的物品。因此,我要为您的self.to_scan.get
,socket.connect
和to_scan.join
来电指定超时时间。
使用logging
计算正在发生的订单事件 - 打印可以从不同的线程交错,但记录器是线程安全的。
此外,像this recipe这样的东西可以方便地为每个线程转储当前的堆栈跟踪。
我没有使用任何支持在Python中调试多个线程的调试器,但有些列出了here。
答案 1 :(得分:1)
很可能不会消耗to_scan队列中的所有项目,也不会调用task_done方法足以解锁ConnectScanner。
可能是在ConnectScan.run的运行时期间抛出异常而你没有捕获并且你的线程过早终止了吗?