win下
pyinstaller --onefile --noconsole your_program.py
在 Windows 系統中,Python 預設會打開一個命令提示視窗來運行程式。如果你希望完全在背景執行並且不顯示視窗,你可以將 Python 程式打包為一個 "無視窗" 的應用程式,使用工具如 pyinstaller。
例如,使用 pyinstaller 打包:
bash
cornflowerblue
udp-sender --async --fec 8x6/64 --max-bitrate 39m --mcast-rdv-addr 224.2.2.1 -mcast-data-addr 224.2.2.2 --interface ip -f file
udp-receiver --nosync --mcast-rdv-addr 224.2.2.1 --interface IP -f file
以下是完整可行的伺服端和客戶端程式,實現了多次啟動 udp-receiver.exe 來接收多個檔案的功能。這些程式將多個檔案依次傳送並接收,確保每個檔案傳輸完成後,進行下一個檔案的傳輸。
伺服端程式將多次使用 udp-sender,依次傳送不同的檔案。
import subprocess
import time
import os
# 檔案清單,這裡是要傳送的多個檔案的路徑
files_to_send = ['file1.txt', 'file2.txt', 'file3.txt']
def send_files(files):
for file in files:
if os.path.exists(file):
print(f"正在傳送檔案: {file}")
command = f'udp-sender --file {file}'
try:
# 使用 udp-sender 傳送檔案
subprocess.run(command, shell=True, check=True)
print(f"{file} 傳送完成。")
except subprocess.CalledProcessError as e:
print(f"傳送 {file} 時發生錯誤: {e}")
time.sleep(2) # 等待 2 秒再傳送下一個檔案
else:
print(f"檔案 {file} 不存在,跳過。")
if __name__ == "__main__":
send_files(files_to_send)
客戶端程式將多次使用 udp-receiver.exe,依次接收不同的檔案。
import subprocess
import time
# 檔案清單,這裡是要接收的多個檔案的名稱(與伺服端傳送的檔案名稱對應)
files_to_receive = ['file1.txt', 'file2.txt', 'file3.txt']
def receive_files(files):
for file in files:
print(f"正在接收檔案: {file}")
command = f'udp-receiver.exe --file {file}'
try:
# 使用 udp-receiver 接收檔案
subprocess.run(command, shell=True, check=True)
print(f"{file} 接收完成。")
except subprocess.CalledProcessError as e:
print(f"接收 {file} 時發生錯誤: {e}")
time.sleep(2) # 等待 2 秒再接收下一個檔案
if __name__ == "__main__":
receive_files(files_to_receive)
伺服端程式:
files_to_send 列表包含了需要傳送的檔案路徑。
每次使用 udp-sender 傳送一個檔案,傳輸完成後會等待 2 秒,再開始下一個檔案的傳輸。
subprocess.run 函數用來執行 udp-sender,這會阻塞程式,直到檔案傳輸完成。
客戶端程式:
files_to_receive 列表包含了需要接收的檔案名稱。這些名稱必須與伺服端傳送的檔案名稱相同。
每次使用 udp-receiver.exe 接收一個檔案,接收完成後會等待 2 秒,再接收下一個檔案。
subprocess.run 同樣用來執行 udp-receiver.exe,程式會在接收完檔案後自動進入下一個檔案的接收流程。
在伺服端電腦上,將要傳送的檔案放置到與程式同一目錄,然後運行伺服端程式,這會自動逐個傳送檔案。
在客戶端電腦上,運行客戶端程式,它會自動逐個接收這些檔案。
這樣的設計能保證伺服端和客戶端都能順序處理多個檔案的傳輸過程,並且每個檔案的傳送和接收都是獨立且有序的。
檔案名稱必須在伺服端和客戶端兩端一致。
如果有需要,可以調整 time.sleep() 的時間,以適應不同的網路延遲或傳輸時間。
0925
使用繁體中文,在w11下,在區域網路中,使用udpcast,"https://www.udpcast.linux.lu/exe.html",這個網站提供的udp-sender.exe,要如何使用python語言,讓使用者在使用ui界面按下"選擇派送檔案"的按鈕來選擇要真實要傳送的檔案,來呼叫udp-sender.exe自動執行,進行傳輸檔案
設定udp-sender.exe的路徑,設定為C:\Windows\System32\udp-receiver.exe,或是也可以抓python執行檔的相同目錄
import os
import tkinter as tk
from tkinter import filedialog
import subprocess
import sys
# 設定固定路徑或動態取得Python執行檔所在目錄的路徑
use_system32_path = True # 設定True則使用C:\Windows\System32,False則使用與Python執行檔相同目錄
if use_system32_path:
udp_sender_path = "C:/Windows/System32/udp-sender.exe"
else:
script_dir = os.path.dirname(sys.executable)
udp_sender_path = os.path.join(script_dir, "udp-sender.exe")
# 建立主窗口
root = tk.Tk()
root.title("UDP 檔案傳輸工具")
# 設定窗口大小
root.geometry("400x150")
# 選擇檔案的函數
def select_file():
file_path = filedialog.askopenfilename(title="選擇要傳送的檔案")
if file_path:
start_udp_sender(file_path)
# 呼叫udp-sender.exe進行檔案傳輸
def start_udp_sender(file_path):
# 使用subprocess來執行udp-sender.exe
command = [udp_sender_path, "--file", file_path, "--port", "8600"]
try:
subprocess.run(command, check=True)
result_label.config(text="檔案傳輸已開始", fg="green")
except subprocess.CalledProcessError as e:
result_label.config(text=f"檔案傳輸失敗: {str(e)}", fg="red")
# 建立按鈕與標籤
select_button = tk.Button(root, text="選擇派送檔案", command=select_file)
select_button.pack(pady=20)
# 顯示傳輸狀態的標籤
result_label = tk.Label(root, text="", fg="blue")
result_label.pack(pady=10)
# 啟動主循環
root.mainloop()
並讓使用者可以用ui界面有按鈕來選擇要傳送的檔案,使用的UI界面,要能夠呈現client端啟動udp-receiver.exe後自動連接伺服端並回傳client伺服端client端的ip位置、MAC位置、電腦名稱及是否連線成功這幾個狀態用網格的方式呈現,UI的下方要能夠呈現伺服端傳送檔案的狀態,包含檔案名稱、檔案大小並用14紅色字體呈現,來當伺服端傳送檔案?client端,要如何使用python語言,使用ui界面來呼叫udp-receiver.exe在背景自動執行,啟程後,會自動找到區域中伺服端傳送檔案的伺服端電腦等待,直到伺服端傳送檔案後自動接傳來的檔案,如果成功接收回報伺服端電腦已收到檔案,完成此次檔案傳輸
113.09.26
在設定窗口的下方,留一欄寫名稱:"UDP檔案派送工具",文字置中,大小18字體,藍色,在建立按鈕與標籤的下方,留一欄為傳輸檔案的狀況,顯示的資料為:檔案名稱、檔案大小
import os
import tkinter as tk
from tkinter import filedialog
import subprocess
import sys
# 設定固定路徑或動態取得Python執行檔所在目錄的路徑
use_system32_path = True # 設定True則使用C:\Windows\System32,False則使用與Python執行檔相同目錄
if use_system32_path:
udp_sender_path = "C:/Windows/System32/udp-sender.exe"
else:
script_dir = os.path.dirname(sys.executable)
udp_sender_path = os.path.join(script_dir, "udp-sender.exe")
# 建立主窗口
root = tk.Tk()
root.title("UDP 檔案傳輸工具")
# 設定窗口大小
root.geometry("400x250") # 調整窗口高度以顯示更多內容
# 選擇檔案的函數
def select_file():
file_path = filedialog.askopenfilename(title="選擇要傳送的檔案")
if file_path:
display_file_info(file_path)
start_udp_sender(file_path)
# 顯示檔案名稱和大小的函數
def display_file_info(file_path):
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path)
file_info_label.config(text=f"檔案名稱: {file_name}\n檔案大小: {file_size} bytes")
# 呼叫udp-sender.exe進行檔案傳輸
def start_udp_sender(file_path):
command = [udp_sender_path, "--file", file_path, "--port", "8600"]
try:
subprocess.run(command, check=True)
result_label.config(text="檔案傳輸已開始", fg="green")
except subprocess.CalledProcessError as e:
result_label.config(text=f"檔案傳輸失敗: {str(e)}", fg="red")
# 建立按鈕與標籤
select_button = tk.Button(root, text="選擇派送檔案", command=select_file)
select_button.pack(pady=10)
# 顯示傳輸狀態的標籤
result_label = tk.Label(root, text="", fg="blue")
result_label.pack(pady=10)
# 建立檔案名稱與檔案大小顯示區域
file_info_label = tk.Label(root, text="檔案名稱: \n檔案大小: ", fg="black")
file_info_label.pack(pady=10)
# 標題"UDP檔案派送工具"
title_label = tk.Label(root, text="UDP檔案派送工具", font=("Arial", 18), fg="blue")
title_label.pack(pady=20)
# 啟動主循環
root.mainloop()
113.09.26
接收端應該使用 udp-receiver.exe 來接收檔案,同樣的可以使用 Python 來自動化此過程,要如何處理?
import os
import tkinter as tk
from tkinter import filedialog
import subprocess
import sys
# 設定固定路徑或動態取得Python執行檔所在目錄的路徑
use_system32_path = True # 設定True則使用C:\Windows\System32,False則使用與Python執行檔相同目錄
if use_system32_path:
udp_receiver_path = "C:/Windows/System32/udp-receiver.exe"
else:
script_dir = os.path.dirname(sys.executable)
udp_receiver_path = os.path.join(script_dir, "udp-receiver.exe")
# 建立主窗口
root = tk.Tk()
root.title("UDP 檔案接收工具")
# 設定窗口大小
root.geometry("400x200")
# 選擇儲存檔案的資料夾
def select_folder():
folder_path = filedialog.askdirectory(title="選擇儲存檔案的資料夾")
if folder_path:
start_udp_receiver(folder_path)
# 呼叫udp-receiver.exe來接收檔案
def start_udp_receiver(folder_path):
command = [udp_receiver_path, "--file", os.path.join(folder_path, "received_file"), "--port", "8600"]
try:
subprocess.run(command, check=True)
result_label.config(text="檔案接收已完成", fg="green")
except subprocess.CalledProcessError as e:
result_label.config(text=f"檔案接收失敗: {str(e)}", fg="red")
# 建立按鈕與標籤
select_button = tk.Button(root, text="選擇儲存資料夾並接收檔案", command=select_folder)
select_button.pack(pady=20)
# 顯示接收狀態的標籤
result_label = tk.Label(root, text="", fg="blue")
result_label.pack(pady=10)
# 標題"UDP檔案接收工具"
title_label = tk.Label(root, text="UDP檔案接收工具", font=("Arial", 18), fg="blue")
title_label.pack(pady=20)
# 啟動主循環
root.mainloop()
伺服端
import tkinter as tk
from tkinter import filedialog, messagebox
import subprocess
import socket
def select_file():
file_path = filedialog.askopenfilename()
file_entry.delete(0, tk.END)
file_entry.insert(0, file_path)
def start_sender():
file_path = file_entry.get()
if not file_path:
messagebox.showerror("錯誤", "請選擇檔案")
return
# 启动udp-sender.exe
sender_command = f'udp-sender.exe {file_path}'
subprocess.Popen(sender_command, shell=True)
status_label.config(text=f"正在傳送檔案: {file_path}")
# GUI
root = tk.Tk()
root.title("UDP Sender")
file_entry = tk.Entry(root, width=50)
file_entry.pack(pady=10)
select_button = tk.Button(root, text="選擇檔案", command=select_file)
select_button.pack(pady=5)
send_button = tk.Button(root, text="開始傳送", command=start_sender)
send_button.pack(pady=20)
status_label = tk.Label(root, text="", fg="red", font=("Arial", 14))
status_label.pack(pady=10)
root.mainloop()
import tkinter as tk
import subprocess
import socket
import threading
def get_ip_mac():
hostname = socket.gethostname()
ip_address = socket.gethostbyname(hostname)
mac_address = ':'.join(['{:02x}'.format((socket.gethostbyname_ex(socket.getfqdn())[2][0] >> elements) & 0xff) for elements in range(0, 6)])
return ip_address, mac_address, hostname
def start_receiver():
receiver_command = 'udp-receiver.exe'
subprocess.Popen(receiver_command, shell=True)
ip, mac, hostname = get_ip_mac()
info_label.config(text=f"IP: {ip}\nMAC: {mac}\nHostname: {hostname}\n連線狀態: 成功")
# GUI
root = tk.Tk()
root.title("UDP Receiver")
info_label = tk.Label(root, text="", font=("Arial", 12))
info_label.pack(pady=10)
start_button = tk.Button(root, text="啟動接收", command=start_receiver)
start_button.pack(pady=20)
root.mainloop()
udp-sender.exe <檔案路徑> -n <接收端 IP 地址> -p <接收端埠>
udp-sender.exe C:\files\example.txt -n 192.168.1.10 -p 9000
udp-receiver.exe -p <接收端埠> -o <儲存檔案的路徑>
udp-receiver.exe -p 9000 -o C:\files\received_file
用繁體中文,在w11下,在區域網路中,使用udpcast,"https://www.udpcast.linux.lu/exe.html",這個網站提供的udp-receiver.exe,要如何下指令才能接收伺服端要傳送的檔案,如果要用python的ui界面來呼叫傳送檔案要如何處理?
import tkinter as tk
import subprocess
import os
def start_receiver():
port = port_entry.get()
output_dir = output_entry.get()
if not port or not output_dir:
tk.messagebox.showerror("錯誤", "請填寫所有欄位")
return
if not os.path.exists(output_dir):
tk.messagebox.showerror("錯誤", "指定的儲存路徑不存在")
return
# 執行 udp-receiver.exe
receiver_command = f'udp-receiver.exe -p {port} -o {output_dir}'
subprocess.Popen(receiver_command, shell=True)
status_label.config(text="接收器已啟動,等待接收檔案...", fg="green")
# GUI
root = tk.Tk()
root.title("UDP Receiver")
tk.Label(root, text="接收埠:").pack(pady=5)
port_entry = tk.Entry(root)
port_entry.pack(pady=5)
tk.Label(root, text="儲存檔案的路徑:").pack(pady=5)
output_entry = tk.Entry(root)
output_entry.pack(pady=5)
start_button = tk.Button(root, text="啟動接收", command=start_receiver)
start_button.pack(pady=20)
status_label = tk.Label(root, text="", font=("Arial", 12))
status_label.pack(pady=10)
root.mainloop()
113.09.26
用繁體中文,在w11下用python語言使用UI界面,在區域網路中伺服端電腦,在UI界面程式啟後呼叫udpcast的upd-sender.exe程式,啟動後要先確認區網中的clinet端也啟動upd-receiver.exe後到伺服端電腦報到,並呈現在伺服端的UI界面中,伺服端的UI界面中需要呈現client端的ip位置、mac位置及電腦名稱,"https://www.udpcast.linux.lu/exe.html",這個網站提供的udp-receiver.exe,及這個網站"http://www.udpcast.linux.lu/cmd.html"提及的指令,要如何下指令才能傳送伺服端要傳送的檔案,並且使用者可以不斷點選並傳送檔案,如果要用python的ui界面來呼叫傳送檔案要如何處理?
113.09.26網路廣播可以收到區網的電腦上線回傳IP
伺服端
import socket
import threading
import tkinter as tk
from tkinter import ttk
import time
# 定義伺服端
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name"), show='headings')
self.tree.heading("IP", text="IP 位置")
self.tree.heading("MAC", text="MAC 位置")
self.tree.heading("Name", text="電腦名稱")
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典,方便檢查與更新
self.client_data_dict = {}
# 建立廣播按鈕
self.broadcast_button = tk.Button(self.root, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(pady=10)
# 取得伺服端的本地IP
self.server_ip = self.get_local_ip()
# 啟動伺服器
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server_socket.bind(("", 37020))
# 啟動接收客戶端報到的線程
threading.Thread(target=self.receive_clients, daemon=True).start()
# 啟動時廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def get_local_ip(self):
# 取得伺服端的本地 IP
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 檢查收到的資料是否完整,防止IndexError
if len(client_data) == 3:
client_ip = client_data[0]
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_data[1], client_data[2])
else:
print("接收到不完整的資料:", client_data)
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name):
# 檢查 IP 或 MAC 是否已經存在
if ip in self.client_data_dict or mac in self.client_data_dict:
# 如果已經存在,更新該行的資料
item_id = self.client_data_dict.get(ip) or self.client_data_dict.get(mac)
self.tree.item(item_id, values=(ip, mac, name))
else:
# 如果不存在,插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
# 廣播伺服器存在,持續30秒
start_time = time.time()
while time.time() - start_time < 30:
try:
self.server_socket.sendto(b"Server here", ('<broadcast>', 37020))
time.sleep(5)
except Exception as e:
print(f"廣播時發生錯誤: {e}")
print("廣播已停止")
def start_broadcast_manually(self):
# 手動啟動廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()
接上
client端
import socket
import threading
import time
import uuid
# 獲取本地 MAC 地址
def get_mac_address():
mac = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0,2*6,2)][::-1])
return mac
# 獲取本機名稱
def get_computer_name():
return socket.gethostname()
# 發送報到給伺服器
def send_report():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
while True:
client_socket.sendto(
f"{socket.gethostbyname(socket.gethostname())},{get_mac_address()},{get_computer_name()}".encode(),
('<broadcast>', 37020)
)
time.sleep(5) # 每5秒報到一次
# 啟動客戶端
if __name__ == "__main__":
threading.Thread(target=send_report, daemon=True).start()
# 保持程式在背景執行
while True:
time.sleep(1)
113.09.26網路廣播可以收到區網的電腦上線回傳IP,增加上線狀態
伺服端
import socket
import threading
import tkinter as tk
from tkinter import ttk
import time
# 定義伺服端
class Server:
def __init__(self, root):
self.root = root
self.root.title("伺服端")
# 建立表格,新增「上線狀態」欄位
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
self.tree.heading("IP", text="IP 位置")
self.tree.heading("MAC", text="MAC 位置")
self.tree.heading("Name", text="電腦名稱")
self.tree.heading("Status", text="上線狀態")
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典,方便檢查與更新
self.client_data_dict = {}
# 建立廣播按鈕
self.broadcast_button = tk.Button(self.root, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(pady=10)
# 取得伺服端的本地IP
self.server_ip = self.get_local_ip()
# 啟動伺服器
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server_socket.bind(("", 37020))
# 啟動接收客戶端報到的線程
threading.Thread(target=self.receive_clients, daemon=True).start()
# 啟動時廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def get_local_ip(self):
# 取得伺服端的本地 IP
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 檢查收到的資料是否完整,防止IndexError
if len(client_data) == 3:
client_ip = client_data[0]
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_data[1], client_data[2])
else:
print("接收到不完整的資料:", client_data)
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name):
# 檢查 IP 或 MAC 是否已經存在
if ip in self.client_data_dict or mac in self.client_data_dict:
# 如果已經存在,更新該行的資料
item_id = self.client_data_dict.get(ip) or self.client_data_dict.get(mac)
self.tree.item(item_id, values=(ip, mac, name, "在線"))
else:
# 如果不存在,插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, "在線"))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
# 廣播伺服器存在,持續30秒
start_time = time.time()
while time.time() - start_time < 30:
try:
self.server_socket.sendto(b"Server here", ('<broadcast>', 37020))
time.sleep(5)
except Exception as e:
print(f"廣播時發生錯誤: {e}")
print("廣播已停止")
def start_broadcast_manually(self):
# 手動啟動廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()
伺服端程式碼,在建立表格的的部份,再新增一個上線狀態,接在電腦名稱後面
伺服端程式碼,增加"啟動選定Client"按鈕、"停止選定Client"按鈕、"全選"按鈕、"全不選"按鈕,這幾個按鈕放在同一橫排
113.09.26網路廣播可以收到區網的電腦上線回傳IP,增加上線狀態,增加"啟動選定Client"按鈕、"停止選定Client"按鈕、"全選"按鈕、"全不選"按鈕,這幾個按鈕放在同一橫排,保留手動廣播功能
import socket
import threading
import tkinter as tk
from tkinter import ttk
import time
# 定義伺服端
class Server:
def __init__(self, root):
self.root = root
self.root.title("伺服端")
# 建立表格,新增「上線狀態」欄位
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
self.tree.heading("IP", text="IP 位置")
self.tree.heading("MAC", text="MAC 位置")
self.tree.heading("Name", text="電腦名稱")
self.tree.heading("Status", text="上線狀態")
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典,方便檢查與更新
self.client_data_dict = {}
# 按鈕框架
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 按鈕
self.start_button = tk.Button(button_frame, text="啟動選定Client", command=self.start_selected_clients)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(button_frame, text="停止選定Client", command=self.stop_selected_clients)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.select_all_button = tk.Button(button_frame, text="全選", command=self.select_all_clients)
self.select_all_button.pack(side=tk.LEFT, padx=5)
self.deselect_all_button = tk.Button(button_frame, text="全不選", command=self.deselect_all_clients)
self.deselect_all_button.pack(side=tk.LEFT, padx=5)
# 建立廣播按鈕
self.broadcast_button = tk.Button(self.root, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(pady=10)
# 取得伺服端的本地IP
self.server_ip = self.get_local_ip()
# 啟動伺服器
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server_socket.bind(("", 37020))
# 啟動接收客戶端報到的線程
threading.Thread(target=self.receive_clients, daemon=True).start()
# 啟動時廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def get_local_ip(self):
# 取得伺服端的本地 IP
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 檢查收到的資料是否完整,防止IndexError
if len(client_data) == 3:
client_ip = client_data[0]
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_data[1], client_data[2])
else:
print("接收到不完整的資料:", client_data)
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name):
# 檢查 IP 或 MAC 是否已經存在
if ip in self.client_data_dict or mac in self.client_data_dict:
# 如果已經存在,更新該行的資料
item_id = self.client_data_dict.get(ip) or self.client_data_dict.get(mac)
self.tree.item(item_id, values=(ip, mac, name, "在線"))
else:
# 如果不存在,插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, "在線"))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
# 廣播伺服器存在,持續30秒
start_time = time.time()
while time.time() - start_time < 30:
try:
self.server_socket.sendto(b"Server here", ('<broadcast>', 37020))
time.sleep(5)
except Exception as e:
print(f"廣播時發生錯誤: {e}")
print("廣播已停止")
def start_broadcast_manually(self):
# 手動啟動廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def start_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"啟動 Client: {name} ({ip})")
def stop_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"停止 Client: {name} ({ip})")
def select_all_clients(self):
for item in self.tree.get_children():
self.tree.selection_add(item)
def deselect_all_clients(self):
self.tree.selection_remove(self.tree.get_children())
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()
113.09.26
伺服端程式碼,增加"選擇檔案並派送"按鈕、增加"選擇目錄並派送"按鈕,接在手動廣播伺服器後面
新增按鈕:
選擇檔案並派送:用戶可以選擇一個檔案進行派送。選擇後會在控制台顯示所選檔案的路徑。
選擇目錄並派送:用戶可以選擇一個目錄進行派送。選擇後會在控制台顯示所選目錄的路徑。
檔案與目錄的選擇:使用 filedialog 模組提供的功能來選擇檔案或目錄,這樣可以更方便用戶選擇欲派送的內容。
在 send_file 和 send_directory 方法中,你可以加入具體的檔案或目錄派送邏輯,以符合你的需求。如果需要進一步的功能或有其他問題,隨時告訴我!
import socket
import threading
import tkinter as tk
from tkinter import ttk, filedialog
import time
# 定義伺服端
class Server:
def __init__(self, root):
self.root = root
self.root.title("伺服端")
# 建立表格,新增「上線狀態」欄位
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
self.tree.heading("IP", text="IP 位置")
self.tree.heading("MAC", text="MAC 位置")
self.tree.heading("Name", text="電腦名稱")
self.tree.heading("Status", text="上線狀態")
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典,方便檢查與更新
self.client_data_dict = {}
# 按鈕框架
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 控制按鈕
self.start_button = tk.Button(button_frame, text="啟動選定Client", command=self.start_selected_clients)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(button_frame, text="停止選定Client", command=self.stop_selected_clients)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.select_all_button = tk.Button(button_frame, text="全選", command=self.select_all_clients)
self.select_all_button.pack(side=tk.LEFT, padx=5)
self.deselect_all_button = tk.Button(button_frame, text="全不選", command=self.deselect_all_clients)
self.deselect_all_button.pack(side=tk.LEFT, padx=5)
# 建立廣播按鈕
self.broadcast_button = tk.Button(self.root, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(pady=10)
# 新增檔案及目錄派送的按鈕框架
send_frame = tk.Frame(self.root)
send_frame.pack(pady=5)
self.send_file_button = tk.Button(send_frame, text="選擇檔案並派送", command=self.send_file)
self.send_file_button.pack(side=tk.LEFT, padx=5)
self.send_directory_button = tk.Button(send_frame, text="選擇目錄並派送", command=self.send_directory)
self.send_directory_button.pack(side=tk.LEFT, padx=5)
# 取得伺服端的本地IP
self.server_ip = self.get_local_ip()
# 啟動伺服器
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server_socket.bind(("", 37020))
# 啟動接收客戶端報到的線程
threading.Thread(target=self.receive_clients, daemon=True).start()
# 啟動時廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def get_local_ip(self):
# 取得伺服端的本地 IP
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 檢查收到的資料是否完整,防止IndexError
if len(client_data) == 3:
client_ip = client_data[0]
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_data[1], client_data[2])
else:
print("接收到不完整的資料:", client_data)
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name):
# 檢查 IP 或 MAC 是否已經存在
if ip in self.client_data_dict or mac in self.client_data_dict:
# 如果已經存在,更新該行的資料
item_id = self.client_data_dict.get(ip) or self.client_data_dict.get(mac)
self.tree.item(item_id, values=(ip, mac, name, "在線"))
else:
# 如果不存在,插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, "在線"))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
# 廣播伺服器存在,持續30秒
start_time = time.time()
while time.time() - start_time < 30:
try:
self.server_socket.sendto(b"Server here", ('<broadcast>', 37020))
time.sleep(5)
except Exception as e:
print(f"廣播時發生錯誤: {e}")
print("廣播已停止")
def start_broadcast_manually(self):
# 手動啟動廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def start_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"啟動 Client: {name} ({ip})")
def stop_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"停止 Client: {name} ({ip})")
def select_all_clients(self):
for item in self.tree.get_children():
self.tree.selection_add(item)
def deselect_all_clients(self):
self.tree.selection_remove(self.tree.get_children())
def send_file(self):
# 打開檔案選擇對話框
file_path = filedialog.askopenfilename()
if file_path:
print(f"選擇的檔案: {file_path}")
# 在這裡增加檔案派送的邏輯
def send_directory(self):
# 打開目錄選擇對話框
dir_path = filedialog.askdirectory()
if dir_path:
print(f"選擇的目錄: {dir_path}")
# 在這裡增加目錄派送的邏輯
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()
113.09.26
伺服端
在建立表格的上方,留一欄寫名稱:UDP檔案派送工具,大小18字體,藍色
import socket
import threading
import tkinter as tk
from tkinter import ttk, filedialog
import time
# 定義伺服端
class Server:
def __init__(self, root):
self.root = root
self.root.title("伺服端")
# 標題
self.title_label = tk.Label(root, text="UDP檔案派送工具", font=("Arial", 18), fg="blue")
self.title_label.pack(pady=10)
# 建立表格,新增「上線狀態」欄位
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
self.tree.heading("IP", text="IP 位置")
self.tree.heading("MAC", text="MAC 位置")
self.tree.heading("Name", text="電腦名稱")
self.tree.heading("Status", text="上線狀態")
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典,方便檢查與更新
self.client_data_dict = {}
# 按鈕框架
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 控制按鈕
self.start_button = tk.Button(button_frame, text="啟動選定Client", command=self.start_selected_clients)
self.start_button.pack(side=tk.LEFT, padx=5)
self.stop_button = tk.Button(button_frame, text="停止選定Client", command=self.stop_selected_clients)
self.stop_button.pack(side=tk.LEFT, padx=5)
self.select_all_button = tk.Button(button_frame, text="全選", command=self.select_all_clients)
self.select_all_button.pack(side=tk.LEFT, padx=5)
self.deselect_all_button = tk.Button(button_frame, text="全不選", command=self.deselect_all_clients)
self.deselect_all_button.pack(side=tk.LEFT, padx=5)
# 建立廣播按鈕
self.broadcast_button = tk.Button(self.root, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(pady=10)
# 新增檔案及目錄派送的按鈕框架
send_frame = tk.Frame(self.root)
send_frame.pack(pady=5)
self.send_file_button = tk.Button(send_frame, text="選擇檔案並派送", command=self.send_file)
self.send_file_button.pack(side=tk.LEFT, padx=5)
self.send_directory_button = tk.Button(send_frame, text="選擇目錄並派送", command=self.send_directory)
self.send_directory_button.pack(side=tk.LEFT, padx=5)
# 取得伺服端的本地IP
self.server_ip = self.get_local_ip()
# 啟動伺服器
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server_socket.bind(("", 37020))
# 啟動接收客戶端報到的線程
threading.Thread(target=self.receive_clients, daemon=True).start()
# 啟動時廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def get_local_ip(self):
# 取得伺服端的本地 IP
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 檢查收到的資料是否完整,防止IndexError
if len(client_data) == 3:
client_ip = client_data[0]
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_data[1], client_data[2])
else:
print("接收到不完整的資料:", client_data)
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name):
# 檢查 IP 或 MAC 是否已經存在
if ip in self.client_data_dict or mac in self.client_data_dict:
# 如果已經存在,更新該行的資料
item_id = self.client_data_dict.get(ip) or self.client_data_dict.get(mac)
self.tree.item(item_id, values=(ip, mac, name, "在線"))
else:
# 如果不存在,插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, "在線"))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
# 廣播伺服器存在,持續30秒
start_time = time.time()
while time.time() - start_time < 30:
try:
self.server_socket.sendto(b"Server here", ('<broadcast>', 37020))
time.sleep(5)
except Exception as e:
print(f"廣播時發生錯誤: {e}")
print("廣播已停止")
def start_broadcast_manually(self):
# 手動啟動廣播30秒
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def start_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"啟動 Client: {name} ({ip})")
def stop_selected_clients(self):
selected_items = self.tree.selection()
for item in selected_items:
ip, mac, name, _ = self.tree.item(item, 'values')
print(f"停止 Client: {name} ({ip})")
def select_all_clients(self):
for item in self.tree.get_children():
self.tree.selection_add(item)
def deselect_all_clients(self):
self.tree.selection_remove(self.tree.get_children())
def send_file(self):
# 打開檔案選擇對話框
file_path = filedialog.askopenfilename()
if file_path:
print(f"選擇的檔案: {file_path}")
# 在這裡增加檔案派送的邏輯
def send_directory(self):
# 打開目錄選擇對話框
dir_path = filedialog.askdirectory()
if dir_path:
print(f"選擇的目錄: {dir_path}")
# 在這裡增加目錄派送的邏輯
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()