2013-08-01 133 views
11

我正在製作一個簡單的多線程端口掃描器。它掃描主機上的所有端口並返回開放端口。麻煩是中斷掃描。掃描完成需要很長時間,有時我希望在掃描過程中用C-c殺死程序。麻煩是掃描不會停止。主線程被鎖定在queue.join()上,並且忘記KeyboardInterrupt,直到隊列中的所有數據都被處理,從而對主線程進行解鎖並優雅地退出程序。我的所有線程都被守護進程,所以當主線程死亡時,它們應該與他一起死亡。Python - 無法用KeyboardInterrupt殺死主線程

我嘗試使用信號庫,沒有成功。重寫threading.Thread類,並增加了對正常終止沒有工作方法...主線程就不會在執行queue.join收到一個KeyboardInterrupt()

import threading, sys, Queue, socket 

queue = Queue.Queue() 

def scan(host): 
    while True: 
     port = queue.get() 

     if port > 999 and port % 1000 == 0: 
      print port 
     try: 
      #sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
      #sock.settimeout(2) #you need timeout or else it will try to connect forever! 
      #sock.connect((host, port)) 
      #----OR---- 
      sock = socket.create_connection((host, port), timeout = 2) 

      sock.send('aaa') 
      data = sock.recv(100) 
      print "Port {} open, message: {}".format(port, data) 
      sock.shutdown() 
      sock.close() 
      queue.task_done() 
     except: 
      queue.task_done() 


def main(host): 
    #populate queue 
    for i in range(1, 65536): 
     queue.put(i) 
    #spawn worker threads 
    for port in range(100): 
     t = threading.Thread(target = scan, args = (host,)) 
     t.daemon = True 
     t.start() 

if __name__ == '__main__': 
    host = "" 

    #does input exist? 
    try: 
     host = sys.argv[1] 
    except: 
     print "No argument was recivied!" 
     exit(1) 

    #is input sane? 
    try: 
     host = socket.gethostbyname(host) 
    except: 
     print "Adress does not exist" 
     exit(2) 

    #execute main program and wait for scan to complete 
    main(host) 
    print "Post main() call!" 
    try: 
     queue.join() 
    except KeyboardInterrupt: 
     print "C-C" 
     exit(3) 

編輯:

我已經找到了解決方案使用時間模塊。

#execute main program and wait for scan to complete 
main(host) 

#a little trick. queue.join() makes main thread immune to keyboardinterrupt. So use queue.empty() with time.sleep() 
#queue.empty() is "unreliable" so it may return True a bit earlier then intented. 
#when queue is true, queue.join() is executed, to confirm that all data was processed. 
#not a true solution, you can't interrupt main thread near the end of scan (when queue.empty() returns True) 
try: 
    while True: 
     if queue.empty() == False: 
      time.sleep(1) 
     else: 
      break 
except KeyboardInterrupt: 
    print "Alas poor port scanner..." 
    exit(1) 
queue.join() 
+1

有誰知道,如果有一種方法可以做到這一點沒有真正一會兒?這浪費了大量的CPU,而我正在使用線程從線程來避免一段時間的真實。 'thread.join'是非常完美的,只要我能夠殺死它(沒有CPU時間浪費的無限循環)。有任何想法嗎?我將運行的測試並不要求我殺死它,因爲線程無限期地持續下去(理論上它會一直持續下去),但是,出於測試目的和將來的參考(因爲在另一個術語中執行'pkill python'是一種痛苦每次開窗都會),我真心想知道。謝謝(並且不要說ctrl + z) – dylnmc

+0

如果你在Windows上運行這個,這是「正常的」。 Python解釋器捕獲CTRL-C並設置一個內部標誌,然後等待控制返回到Python解釋,以便它可以引發'KeyboardInterrupt'。它依靠阻止系統調用返回'EINTR'以返回並檢查內部標誌。但是,當發生這種情況時,阻止Windows上的系統操作不會返回'EINTR'錯誤代碼,因此'KeyboardInterrupt'異常會延遲到您的阻止操作完成。 –

回答

0

當您創建的線程將它們添加到正在運行的線程的列表,並使用Ctrl-C打交道時發送kill信號給名單上的每個線程。這樣,你正在積極清理,而不是依靠它爲你做。

4

你已經做出你的線程守護程序,但你必須保持你的主線程活着,而守護線程都在那裏,還有如何做到這一點:Cannot kill Python script with Ctrl-C

+3

我的主線是活着的。 queue.join()阻塞線程,直到隊列中的所有數據都被處理,然後queue.join()解除對主線程和程序的解除阻塞。問題是,當主線程到達queue.join()時,它使線程對KeyboardInterrupt「免疫」。如果我不能殺死主線程,我不能殺死守護線程。我會澄清我的問題... – RedSparrow

+0

也許試試這個:http://stackoverflow.com/a/13570261/2031025 – solusipse

+0

謝謝你的建議,但我找到了解決方案。 – RedSparrow