2011-11-26 151 views
1

我想实现的Python一个simpley端口扫描工具。它通过创建大量工作线程来工作,这些工作线程扫描队列中提供的端口。他们将结果保存在另一个队列中。当扫描所有端口时,线程和应用程序应该终止。这里存在的问题是:对于少数端口一切正常,但如果我尝试扫描200个或更多的端口,应用程序将陷入僵局。我不知道为什么。死锁在Python线程

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() 

有没有人看到什么可能是错的?另外,我会欣赏一些tipps如何在Python中调试这种线程问题。

回答

1

很可能并非所有to_scan队列上的项目都被占用,并且您没有足够多的时间调用task_done方法来解锁ConnectScanner。

难道是在ConnectScan.run的运行时间期间抛出一个异常:您没有捕获并且线程过早终止?

+0

你是对的,tast_done没有被经常调用。原因是如果你尝试连接到一个过滤的端口(即你不会得到任何响应),套接字不会抛出异常,而是永远等待。那是我的僵局。 – j0ker

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 /终于可以肯定的:

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() 

一般来说,调试多线程程序是棘手的。我尽量避免任何无限期封锁 - 最好是有一些碰撞大肆因为超时时间太短,而不是把它仅仅停留永远在等待永远不会出现的项目。所以我想你​​,socket.connectto_scan.join调用指定超时。

使用logging来确定事件发生的顺序 - 打印可以从不同的线程交错,但记录器是线程安全的。

此外,像this recipe这样的东西可以方便地为每个线程转储当前堆栈跟踪。

我还没有使用任何调试器支持在Python中调试多个线程,但也列出了一些here

+0

感谢的窍门。他们帮助我找到解决方案,虽然它不是一个抛出的异常,而是一个没有抛出的异常。我会看看你建议的调试技术。 – j0ker