Python Tkinter學習之端口掃描器的編寫

Python Tkinter之端口掃描器的編寫

這其實是個作業,要求我們使用python GUI界面做一個端口掃描
不過tk之前一直沒有接觸過,所以記錄一下!

圖形化界面編寫

先把圖形化界面編寫完成:

from tkinter import *
import socket
import threading
import time
import queue
import tkinter.messagebox as msgbox

class MyGui():
    def __init__(self, init_window_name):
        self.init_window_name = init_window_name

    def set_init_window(self):                  #初始化窗口
        self.init_window_name.title("端口掃描器 v1.0")	#定義標題
        self.window_center(400, 520)
        self.init_window_name.resizable(0,0)    #固定窗口,禁止拖拉

        # 定義區域,把全局分爲上中下部分
        self.frame_top = Frame(self.init_window_name, width=400, height=200)
        self.frame_center = Frame(self.init_window_name, width=400, height=300)
        self.frame_foot = Frame(self.init_window_name, width=400, height=10)
		
        self.frame_top.grid(row=0, column=0)
        self.frame_center.grid(row=1, column=0)
        self.frame_foot.grid(row=2, column=0, pady="20")

        # 上部分佈局
        self.ip_lb_top = Label(self.frame_top, text="IP地址", font="楷體")
        self.port_lb_top = Label(self.frame_top, text="端口列表", font="楷體")
        self.ip_input = StringVar()
        self.port_input = StringVar()
        self.ip_input.set('127.0.0.1')	#設置輸入框默認的值
        self.port_input.set('1-65535')
        self.ip_content = Entry(self.frame_top, textvariable=self.ip_input, font="楷體")		#輸入框
        self.port_content = Entry(self.frame_top, textvariable=self.port_input, font="楷體")	#輸入框二
        self.PortScan = Button(self.frame_top, text="開始掃描", command=self.runing, font="楷體")	#點擊按鈕,執行self.runing函數
        self.sign = Label(self.frame_top, text="By A_dmin", font="楷體")
        self.ip_lb_top.grid(row=0, column=0, padx=15, pady=20)
        self.ip_content.grid(row=0, column=1, padx=15, pady=20)
        self.port_lb_top.grid(row=1, column=0, padx=15, pady=20)
        self.port_content.grid(row=1, column=1, padx=15, pady=20)
        self.PortScan.grid(row=3, column=0, padx=15, pady=20)
        self.sign.grid(row=3, column=1, padx=15, pady=20)

        # 中間部分佈局
        self.porttext = Text(self.frame_center, width=48, height=22)
        self.scroll = Scrollbar(self.frame_center)		#爲text添加滾動條
        self.scroll.pack(side=RIGHT, fill=Y)
        self.porttext.pack(side=RIGHT, fill=Y)
        self.scroll.config(command=self.porttext.yview)
        self.porttext.config(yscrollcommand=self.scroll.set)
        #self.porttext.grid(row=0, column=0, pady=10)

        # 下部分佈局

    # 窗口居中
    def window_center(self, width, height):
        screenwidth = self.init_window_name.winfo_screenwidth()
        screenheight = self.init_window_name.winfo_screenheight()
        size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.init_window_name.geometry(size)

    # 獲取當前時間
    def get_current_time(self):
        current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
        return current_time
		
    def runing(self):
        pass

if __name__ == "__main__":
    pygui=Tk()
    init_window = MyGui(pygui)
    init_window.set_init_window()
    pygui.mainloop()

運行結果圖:
在這裏插入圖片描述

編寫功能函數

這裏遇到一個重大的問題,就是程序會假死,,,,,
百度了一下原因:tk在做大量的寫入時,會進入假死或無響應狀態,也就是執行耗時長的操作
最後找到解決辦法:繼承多線程類,並且直接在初始化函數中開啓線程,代碼如下

class MyThread(threading.Thread):
        def __init__(self, func, *args):
            super().__init__()

            self.func = func
            self.args = args

            self.setDaemon(True)
            self.start()  # 在這裏開始

        def run(self):
            self.func(*self.args)

解決了如上問題就好辦了,功能函數的編寫就是利用socket還有多線程,外加一個隊列:

def runing(self):
        self.porttext.delete(1.0, END)
        if re.match(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", self.ip_content.get()):
            self.ip = self.ip_content.get()
        else:
            msgbox.showerror(title="Error", message="ip格式錯誤,格式爲:x.x.x.x")
            return
        if re.match(r"^(?:[0-9]{1,5}-){1}[0-9]{1,5}$", self.port_content.get()):
            ports = self.port_content.get()
        else:
            msgbox.showerror(title="Error", message="端口列表格式錯誤,格式爲:xxx-xxx,而且必須是數字")
            return
        ports = ports.split('-')
        startport = ports[0]
        endport = ports[1]
        self.porttext.insert(END, "當前時間爲: "+self.get_current_time()+"\nIP地址:"+self.ip+"\n端口列表:"+self.port_content.get()+"\n開始掃描\n")
        self.portslist = list(range(int(startport), int(endport) + 1))
        port_queue = queue.Queue()
        for port in self.portslist:
            port_queue.put(port)
        for i in range(0, 200):
            self.MyThread(self.scan, port_queue)

    def scan(self, port_queue):
        while True:
            if port_queue.empty():
                break
            ip = self.ip
            port = port_queue.get()
            timeout = 0.5

            try:
                s = socket.socket()
                s.settimeout(timeout)
                s.connect((self.ip, port))
                string = "Port " + str(port) + " is OPEN\n"
                self.porttext.insert(END, string)
                self.porttext.see(END)
            except Exception as e:
                pass
            finally:
                s.close()

綜上代碼就已經書寫完畢了,運行結果如圖:
在這裏插入圖片描述

整體代碼以及生成exe文件

整體代碼如下:

from tkinter import *
import socket
import threading
import time
import queue
import tkinter.messagebox as msgbox

class MyGui():
    def __init__(self, init_window_name):
        self.init_window_name = init_window_name

    def set_init_window(self):                  #初始化窗口
        self.init_window_name.title("端口掃描器 v1.0")
        self.window_center(400, 520)
        self.init_window_name.resizable(0,0)    #固定窗口,禁止拖拉

        # 定義區域,把全局分爲上中下部分
        self.frame_top = Frame(self.init_window_name, width=400, height=200)
        self.frame_center = Frame(self.init_window_name, width=400, height=300)
        self.frame_foot = Frame(self.init_window_name, width=400, height=10)
        self.frame_top.grid(row=0, column=0)
        self.frame_center.grid(row=1, column=0)
        self.frame_foot.grid(row=2, column=0, pady="20")

        # 上部分佈局
        self.ip_lb_top = Label(self.frame_top, text="IP地址", font="楷體")
        self.port_lb_top = Label(self.frame_top, text="端口列表", font="楷體")
        self.ip_input = StringVar()
        self.port_input = StringVar()
        self.ip_input.set('127.0.0.1')
        self.port_input.set('1-65535')
        self.ip_content = Entry(self.frame_top, textvariable=self.ip_input, font="楷體")
        self.port_content = Entry(self.frame_top, textvariable=self.port_input, font="楷體")
        self.PortScan = Button(self.frame_top, text="開始掃描", command=self.runing, font="楷體")
        self.sign = Label(self.frame_top, text="By A_dmin", font="楷體")
        self.ip_lb_top.grid(row=0, column=0, padx=15, pady=20)
        self.ip_content.grid(row=0, column=1, padx=15, pady=20)
        self.port_lb_top.grid(row=1, column=0, padx=15, pady=20)
        self.port_content.grid(row=1, column=1, padx=15, pady=20)
        self.PortScan.grid(row=3, column=0, padx=15, pady=20)
        self.sign.grid(row=3, column=1, padx=15, pady=20)

        # 中間部分佈局
        self.porttext = Text(self.frame_center, width=48, height=22)
        self.scroll = Scrollbar(self.frame_center)
        self.scroll.pack(side=RIGHT, fill=Y)
        self.porttext.pack(side=RIGHT, fill=Y)
        self.scroll.config(command=self.porttext.yview)
        self.porttext.config(yscrollcommand=self.scroll.set)
        #self.porttext.grid(row=0, column=0, pady=10)

        # 下部分佈局

    # 窗口居中
    def window_center(self, width, height):
        screenwidth = self.init_window_name.winfo_screenwidth()
        screenheight = self.init_window_name.winfo_screenheight()
        size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.init_window_name.geometry(size)

    # 獲取當前時間
    def get_current_time(self):
        current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
        return current_time

    class MyThread(threading.Thread):
        def __init__(self, func, *args):
            super().__init__()

            self.func = func
            self.args = args

            self.setDaemon(True)
            self.start()  # 在這裏開始

        def run(self):
            self.func(*self.args)

    def runing(self):
        self.porttext.delete(1.0, END)
        if re.match(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", self.ip_content.get()):
            self.ip = self.ip_content.get()
        else:
            msgbox.showerror(title="Error", message="ip格式錯誤,格式爲:x.x.x.x")
            return
        if re.match(r"^(?:[0-9]{1,5}-){1}[0-9]{1,5}$", self.port_content.get()):
            ports = self.port_content.get()
        else:
            msgbox.showerror(title="Error", message="端口列表格式錯誤,格式爲:xxx-xxx,而且必須是數字")
            return
        ports = ports.split('-')
        startport = ports[0]
        endport = ports[1]
        self.porttext.insert(END, "當前時間爲: "+self.get_current_time()+"\nIP地址:"+self.ip+"\n端口列表:"+self.port_content.get()+"\n開始掃描\n")
        self.portslist = list(range(int(startport), int(endport) + 1))
        port_queue = queue.Queue()
        for port in self.portslist:
            port_queue.put(port)
        for i in range(0, 800):
            self.MyThread(self.scan, port_queue)

    def scan(self, port_queue):
        while True:
            if port_queue.empty():
                break
            ip = self.ip
            port = port_queue.get()
            timeout = 0.5

            try:
                s = socket.socket()
                s.settimeout(timeout)
                s.connect((self.ip, port))
                string = "Port " + str(port) + " is OPEN\n"
                self.porttext.insert(END, string)
                self.porttext.see(END)
            except Exception as e:
                pass
            finally:
                s.close()

if __name__ == "__main__":
    pygui=Tk()
    init_window = MyGui(pygui)
    init_window.set_init_window()
    pygui.mainloop()

生成exe文件我選擇使用的是pyinstaller,這裏在我安裝pyinstaller時出現了問題!!
出現了No module named 'setuptools.build_meta'
這裏使用命令對其進行更新:pip install --upgrade setuptools
隨即即可安裝成功了,打開cmd命令窗口
切換到py文件所在的文件夾,然後使用命令pyinstaller -F -w PortScan.py
在這裏插入圖片描述
之後去文件夾下的dist目錄下就可以看見有個exe文件:
在這裏插入圖片描述
雙擊即可運行!!!
在這裏插入圖片描述
這裏還有個疑問,不知道如果電腦上沒有python或者python沒有安裝對應的模塊是否還能運行,,

總結

程序算是完成了,不知道還是否存在bug,,,不過有個小問題,沒有顯示何時結束,,,
不過速度上還行,由於自己電腦太差不能運行過多的線程,大概只能運行800個,
個人覺得速度挺快的,,,也算好了吧,,,
總的來說收穫還是很大的,對TK有了一些瞭解,不至於像之前一樣一點都不知道
還有在這個過程中出現了很多的問題,問題解決過程中也收穫了很多!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章