2011-11-26 196 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