使用繁體中文,"def select_client_and_file(self): selected_items = self.tree.selection() # 可以選取多個客戶端 if selected_items: client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP # 選擇檔案或資料夾 file_paths = filedialog.askopenfilenames(title="選擇檔案") if file_paths: # 傳送檔案清單給每一個選中的客戶端 for client_ip in client_ips: self.prepare_file_transfer_list(client_ip, file_paths) def prepare_file_transfer_list(self, client_ip, file_paths): transfer_list = "\n".join(file_paths) print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}") # 傳送檔案清單給 client threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start() def send_file_list(self, client_ip, file_paths): # 建立檔案傳輸指令 file_list_str = ",".join(file_paths) try: self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500)) print(f"檔案傳輸清單已傳送至 {client_ip}") # 等待2秒後再使用 udp-sender.exe 傳送檔案 time.sleep(2) # 根據選取的客戶端數量,計算 min-receivers selected_clients = self.tree.selection() client_count = len(selected_clients) # 使用 udp-sender.exe 傳送檔案 for file_path in file_paths: if os.path.exists(file_path): subprocess.run([ 'udp-sender.exe', '--file', file_path, '--port', '8500', '--min-receivers', str(client_count), # 根據選取的客戶端數量設定 '--autostart', '5' # 自動啟動,等待 5 秒 ]) print(f"檔案 {file_path} 已傳送至 {client_ip},並等待 {client_count} 個接收端") except Exception as e: print(f"傳送檔案清單時發生錯誤: {e}")",請問上述程式執行的順序是?
上述程式的執行順序如下:
選擇客戶端和檔案:
當調用 select_client_and_file 方法時,程式首先獲取從 self.tree 控制元件中選中的項目(可以是多個客戶端)。
如果有選中的項目,則提取每個選中項目的客戶端 IP。
選擇檔案:
程式接著會彈出檔案對話框,讓使用者選擇要傳送的檔案。
如果使用者選擇了檔案,程式會將檔案清單傳遞給每個選中的客戶端。
準備檔案傳輸清單:
prepare_file_transfer_list 方法被呼叫,接受客戶端 IP 和檔案路徑作為參數。
這個方法會把檔案路徑轉換為字串並列印出來。
隨後,啟動一個新的執行緒,調用 send_file_list 方法,並傳遞客戶端 IP 和檔案路徑。
發送檔案清單:
在 send_file_list 方法中,程式會將檔案路徑清單組合成一個字串,並通過 UDP 傳送給指定的客戶端。
之後,程式會暫停 2 秒鐘,以便客戶端準備接收檔案。
接著,根據選中的客戶端數量計算 min-receivers。
最後,對於每一個檔案,程式會檢查檔案是否存在,並使用 udp-sender.exe 傳送檔案,並且設置自動啟動和接收端的數量。
錯誤處理:
如果在傳送檔案清單的過程中發生任何錯誤,程式會捕獲並打印錯誤訊息。
這個流程中,關鍵的點是使用執行緒來進行非同步檔案傳輸,讓主程式能夠繼續運行而不會被阻塞。
接上,"發送檔案清單: 在 send_file_list 方法中,程式會將檔案路徑清單組合成一個字串,並通過 UDP 傳送給指定的客戶端。 之後,程式會暫停 2 秒鐘,以便客戶端準備接收檔案。 接著,根據選中的客戶端數量計算 min-receivers。 最後,對於每一個檔案,程式會檢查檔案是否存在,並使用 udp-sender.exe 傳送檔案,並且設置自動啟動和接收端的數量。",在udp-sender.exe 傳送檔案完程有什麼方法可以知道,並做流程控制嗎?
要在使用 udp-sender.exe 傳送檔案後進行流程控制,可以採取以下幾種方法:
返回值或狀態碼檢查:
在使用 subprocess.run() 時,可以捕獲返回的狀態碼。一般來說,udp-sender.exe 會返回一個狀態碼,表示傳送的結果。你可以根據這個狀態碼來決定後續的操作。
result = subprocess.run([...], capture_output=True)
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
檔案傳輸完成的通知:
如果 udp-sender.exe 支持發送完成通知,你可以在客戶端上實作一個接收器,當檔案傳送完成時,它可以發送一個確認訊息回到伺服器。
伺服器收到確認後,可以進行後續的流程控制。
檢查檔案完整性:
你可以在檔案傳送完成後,對檔案進行完整性檢查,比如使用哈希值(如 MD5 或 SHA256)來確保檔案已正確傳送並完整接收。這需要在客戶端和伺服器端都有相關的邏輯來進行比對。
使用旗標或狀態變數:
在傳送檔案的時候,可以設定一個旗標或狀態變數,來記錄檔案傳送的狀態。在傳送完成後更新這個變數,伺服器可以定期檢查這個狀態來進行流程控制。
日誌紀錄:
在傳送檔案時,將傳送的狀態寫入日誌檔案,然後在後續的流程中分析這些日誌,根據需要進行相應的操作。
這些方法可以幫助你實現對檔案傳送過程的監控和控制,以確保整個流程的可靠性和可追蹤性。選擇合適的方法取決於你的應用需求和 udp-sender.exe 的具體行為。
接上,使用"if result.returncode == 0"這個回傳值,有什麼方式可以將回傳值通知"# 選擇檔案或資料夾 file_paths = filedialog.askopenfilenames(title="選擇檔案") if file_paths: # 傳送檔案清單給每一個選中的客戶端 for client_ip in client_ips: self.prepare_file_transfer_list(client_ip, file_paths) def prepare_file_transfer_list(self, client_ip, file_paths): transfer_list = "\n".join(file_paths) print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}") # 傳送檔案清單給 client threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()"這段程式,準備進行第二檔案的傳輸?
要在第一個檔案傳輸完成後再開始第二個檔案的傳輸,你可以使用回傳值來通知主流程,確保第一個檔案的傳輸成功後再進行下一個檔案的傳輸。這裡有一個簡單的示例來展示如何實現這個流程:
使用 Queue 傳遞狀態:
使用 queue.Queue 將傳輸狀態從執行緒中傳回主執行緒。
修改 send_file_list 方法:
在 send_file_list 方法中,將結果傳回主執行緒。
以下是修改後的示例程式碼:
import queue
import threading
import subprocess
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1', # 設置為 1,根據需要可以更改
'--autostart', '5'
], capture_output=True)
# 將回傳值放入 queue
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
# 處理傳輸結果
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
# 如果成功,繼續進行下個檔案的傳輸
print(f"{file_path} 傳輸成功,準備傳送下一個檔案...")
else:
# 處理失敗的情況
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
queue.Queue: 使用這個佇列來儲存傳輸的結果。
self.transfer_queue.put(...): 將檔案路徑和回傳碼放入佇列。
process_transfer_results 方法: 這個方法可以在主執行緒中定期呼叫,來檢查佇列中是否有新的傳輸結果,根據結果來控制接下來的傳輸。
在適當的地方(例如傳輸完成後或在 UI 更新時),呼叫 process_transfer_results 來檢查傳輸狀態,然後決定是否進行下一個檔案的傳輸。這樣可以確保傳輸的控制流是有序的。
接上,那進行下一輪傳輸前,"# 選擇檔案或資料夾 file_paths = filedialog.askopenfilenames(title="選擇檔案")"需要根據這個先把多個檔案分成多個檔案清單,每一輪執行一個清單,要如何修正這個程式?
要將多個檔案分成多個檔案清單,每一輪執行一個清單,可以修改 select_client_and_file 方法,以便在選擇檔案後將檔案分組,然後在每一輪傳輸時執行一個檔案清單。以下是修改後的示例程式碼:
將檔案分組:可以根據你希望的每個清單的大小來分組檔案。
在傳輸完成後開始下一輪傳輸。
這裡的範例將每五個檔案作為一個清單進行傳輸,你可以根據需要調整這個數量。
import queue
import threading
import subprocess
import os
import time
from tkinter import filedialog
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 將檔案分組,每 5 個檔案一組
self.file_lists = [file_paths[i:i + 5] for i in range(0, len(file_paths), 5)]
self.current_file_list_index = 0 # 重置索引
self.send_next_file_list() # 開始傳輸第一個檔案清單
def send_next_file_list(self):
if self.current_file_list_index < len(self.file_lists):
file_paths = self.file_lists[self.current_file_list_index]
for client_ip in self.client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
else:
print("所有檔案已傳送完成。")
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1',
'--autostart', '5'
], capture_output=True)
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
# 傳送完成後更新當前檔案清單索引
self.current_file_list_index += 1
self.send_next_file_list() # 開始下一個檔案清單
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
print(f"{file_path} 傳輸成功。")
else:
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
分組檔案:
使用 self.file_lists 來儲存每一組檔案,這裡以每五個檔案為一組,你可以根據需求調整這個數量。
傳送下一個檔案清單:
在 send_file_list 方法中,傳送完一組檔案後,自動調用 send_next_file_list 方法開始傳送下一組檔案。
流程控制:
每當完成一組檔案的傳送後,程序會自動檢查是否還有更多的檔案清單可供傳送,從而確保傳輸過程的有序進行。
這樣的設計可以方便地管理多組檔案的傳輸,確保每組檔案傳送完成後再開始下一組。
接上," # 選擇檔案或資料夾 file_paths = filedialog.askopenfilenames(title="選擇檔案") if file_paths: # 將檔案分組,每 5 個檔案一組 self.file_lists = [file_paths[i:i + 5] for i in range(0, len(file_paths), 5)] self.current_file_list_index = 0 # 重置索引 self.send_next_file_list() # 開始傳輸第一個檔案清單",檔案分組能跟據使用者選擇檔案數量來決定,要如何修正程式?
要根據使用者選擇的檔案數量來決定檔案的分組,可以在 select_client_and_file 方法中添加一個參數,讓使用者輸入每組檔案的數量。這裡的修改會包括:
讓使用者輸入分組大小:可以使用 tkinter.simpledialog 來請求使用者輸入。
依據使用者的輸入來分組檔案。
以下是修改後的示例程式碼:
import queue
import threading
import subprocess
import os
import time
from tkinter import filedialog
from tkinter import simpledialog
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 讓使用者輸入每組檔案的大小
group_size = simpledialog.askinteger("檔案分組大小", "請輸入每組檔案的大小:", minvalue=1, maxvalue=len(file_paths))
if group_size is not None: # 如果使用者提供了有效的分組大小
# 將檔案分組
self.file_lists = [file_paths[i:i + group_size] for i in range(0, len(file_paths), group_size)]
self.current_file_list_index = 0 # 重置索引
self.send_next_file_list() # 開始傳輸第一個檔案清單
def send_next_file_list(self):
if self.current_file_list_index < len(self.file_lists):
file_paths = self.file_lists[self.current_file_list_index]
for client_ip in self.client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
else:
print("所有檔案已傳送完成。")
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1',
'--autostart', '5'
], capture_output=True)
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
# 傳送完成後更新當前檔案清單索引
self.current_file_list_index += 1
self.send_next_file_list() # 開始下一個檔案清單
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
print(f"{file_path} 傳輸成功。")
else:
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
使用 simpledialog:在選擇檔案後,使用 simpledialog.askinteger 讓使用者輸入每組檔案的大小,並設定最小值和最大值。
分組檔案:根據使用者輸入的 group_size,將檔案進行分組。
有效性檢查:確認使用者輸入的分組大小是有效的(例如大於 0 且不超過選擇的檔案數量)。
這樣的設計使得使用者可以靈活決定檔案的分組方式,並且程序會根據這些設定來執行傳輸。
待測備用程式
import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk
import time
import os
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(pady=10)
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10)
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
self.status_label = tk.Label(self.root, text="目前傳輸狀態:等待中", font=("Arial", 12), fg="black")
self.status_label.pack(pady=10)
# 新增備註區域,左右兩格
self.footer_frame = tk.Frame(self.root)
self.footer_frame.pack(pady=20, fill=tk.X)
# 左邊格 - 跑馬燈效果,文字顏色改為gold
self.marquee_text = "感謝ChatGPT 感謝UDPcast "
self.marquee_label = tk.Label(self.footer_frame, text=self.marquee_text, font=("Arial", 12), fg="gold")
self.marquee_label.pack(side=tk.LEFT, padx=10)
# 右邊格 - 固定文字,文字顏色改為gold
self.fixed_label = tk.Label(self.footer_frame, text="製作者:WCS 日期:2024.10.01", font=("Arial", 12), fg="gold")
self.fixed_label.pack(side=tk.RIGHT, padx=10)
# 啟動跑馬燈
self.move_text()
# 取得伺服端的本地 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):
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def move_text(self):
# 跑馬燈效果,將字串每次往左移動一個字符
self.marquee_text = self.marquee_text[1:] + self.marquee_text[0]
self.marquee_label.config(text=self.marquee_text)
self.root.after(200, self.move_text) # 每200毫秒移動一次
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 確認收到的資料是否完整
if len(client_data) == 3:
client_ip, client_mac, client_name = client_data
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_mac, client_name, "Online")
else:
print(f"接收到不完整的資料:{client_data}")
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name, status):
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, status))
else:
# 插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, status))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
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):
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 更新狀態為正在準備傳送
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")
if __name__ == "__main__":
root = tk.Tk()
server_app = Server(root)
root.mainloop()
詢問
"import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk
import time
import os
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(pady=10)
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10)
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
self.status_label = tk.Label(self.root, text="目前傳輸狀態:等待中", font=("Arial", 12), fg="black")
self.status_label.pack(pady=10)
# 新增備註區域,左右兩格
self.footer_frame = tk.Frame(self.root)
self.footer_frame.pack(pady=20, fill=tk.X)
# 左邊格 - 跑馬燈效果,文字顏色改為gold
self.marquee_text = "感謝ChatGPT 感謝UDPcast "
self.marquee_label = tk.Label(self.footer_frame, text=self.marquee_text, font=("Arial", 12), fg="gold")
self.marquee_label.pack(side=tk.LEFT, padx=10)
# 右邊格 - 固定文字,文字顏色改為gold
self.fixed_label = tk.Label(self.footer_frame, text="製作者:WCS 日期:2024.10.01", font=("Arial", 12), fg="gold")
self.fixed_label.pack(side=tk.RIGHT, padx=10)
# 啟動跑馬燈
self.move_text()
# 取得伺服端的本地 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):
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def move_text(self):
# 跑馬燈效果,將字串每次往左移動一個字符
self.marquee_text = self.marquee_text[1:] + self.marquee_text[0]
self.marquee_label.config(text=self.marquee_text)
self.root.after(200, self.move_text) # 每200毫秒移動一次
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 確認收到的資料是否完整
if len(client_data) == 3:
client_ip, client_mac, client_name = client_data
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_mac, client_name, "Online")
else:
print(f"接收到不完整的資料:{client_data}")
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name, status):
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, status))
else:
# 插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, status))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
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):
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 更新狀態為正在準備傳送
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")
if __name__ == "__main__":
root = tk.Tk()
server_app = Server(root)
root.mainloop()",使用繁體中文,希望修正,當使用者"# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")"選擇多個檔案後,如果是3個檔案就轉成3個檔案清單,拿出第1個檔案清單,就接著執行程式,剩下的2個檔案單先暫存,等"# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")"這個返回值或狀態碼回傳已完成,使剛剛的第2個檔案清單,再進行"def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 更新狀態為正在準備傳送
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")"這段程式,但一開始選過的client端電腦就不再選用,能利用queue.Queue: 使用這個佇列來儲存傳輸的結果
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引,來協助修正整個程式嗎?謝謝
queue.Queue: 使用這個佇列來儲存傳輸的結果
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk
import time
import os
import subprocess
import queue # 引入 queue 模組
class Server:
def __init__(self, root):
self.root = root
self.root.title("伺服器")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(pady=10)
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10)
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
self.status_label = tk.Label(self.root, text="目前傳輸狀態:等待中", font=("Arial", 12), fg="black")
self.status_label.pack(pady=10)
# 新增佇列來儲存傳輸結果
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
# 建立伺服器廣播和接收功能
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):
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def move_text(self):
self.marquee_text = self.marquee_text[1:] + self.marquee_text[0]
self.marquee_label.config(text=self.marquee_text)
self.root.after(200, self.move_text)
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
if len(client_data) == 3:
client_ip, client_mac, client_name = client_data
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_mac, client_name, "Online")
else:
print(f"接收到不完整的資料:{client_data}")
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name, status):
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, status))
else:
item_id = self.tree.insert("", "end", values=(ip, mac, name, status))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
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):
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items]
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
self.file_lists = list(file_paths) # 存儲檔案清單
self.current_file_list_index = 0 # 初始化索引
self.start_file_transfer() # 開始傳送檔案
def start_file_transfer(self):
if self.current_file_list_index < len(self.file_lists):
file_path = self.file_lists[self.current_file_list_index]
if os.path.exists(file_path):
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)}")
# 傳送檔案給所有選中的客戶端
for client_ip in self.client_ips:
self.prepare_file_transfer(client_ip, file_path)
def prepare_file_transfer(self, client_ip, file_path):
transfer_list = file_path
print(f"準備傳輸檔案到 {client_ip}: {transfer_list}")
threading.Thread(target=self.send_file_list, args=(client_ip, file_path), daemon=True).start()
def send_file_list(self, client_ip, file_path):
try:
self.server_socket.sendto(file_path.encode(), (client_ip, 8500))
time.sleep(2)
selected_clients = len(self.client_ips)
file_size = os.path.getsize(file_path)
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {selected_clients} --nokbd --autostart 3"
result = subprocess.run(command, shell=True)
if result.returncode == 0:
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
self.transfer_queue.put("成功") # 傳輸結果放入佇列
self.current_file_list_index += 1 # 更新索引
self.start_file_transfer() # 繼續下一個檔案
else:
self.status_label.config(text="傳輸失敗!")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")
if __name__ == "__main__":
root = tk.Tk()
server_app = Server(root)
root.mainloop()
還是失敗
import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk
import time
import os
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(pady=10)
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10)
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
self.status_label = tk.Label(self.root, text="目前傳輸狀態:等待中", font=("Arial", 12), fg="black")
self.status_label.pack(pady=10)
# 新增備註區域,左右兩格
self.footer_frame = tk.Frame(self.root)
self.footer_frame.pack(pady=20, fill=tk.X)
# 左邊格 - 跑馬燈效果,文字顏色改為gold
self.marquee_text = "感謝ChatGPT 感謝UDPcast "
self.marquee_label = tk.Label(self.footer_frame, text=self.marquee_text, font=("Arial", 12), fg="gold")
self.marquee_label.pack(side=tk.LEFT, padx=10)
# 右邊格 - 固定文字,文字顏色改為gold
self.fixed_label = tk.Label(self.footer_frame, text="製作者:WCS 日期:2024.10.01", font=("Arial", 12), fg="gold")
self.fixed_label.pack(side=tk.RIGHT, padx=10)
# 啟動跑馬燈
self.move_text()
# 取得伺服端的本地 IP
self.server_ip = self.get_local_ip()
# 儲存檔案清單
self.file_lists = []
self.current_file_list_index = 0
self.client_ips = []
# 啟動伺服器
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):
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def move_text(self):
# 跑馬燈效果,將字串每次往左移動一個字符
self.marquee_text = self.marquee_text[1:] + self.marquee_text[0]
self.marquee_label.config(text=self.marquee_text)
self.root.after(200, self.move_text) # 每200毫秒移動一次
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
if len(client_data) == 3:
client_ip, client_mac, client_name = client_data
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_mac, client_name, "Online")
else:
print(f"接收到不完整的資料:{client_data}")
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name, status):
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, status))
else:
# 插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, status))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
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):
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 儲存檔案清單
self.file_lists = list(file_paths)
self.current_file_list_index = 0
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
self.start_file_transfer()
def start_file_transfer(self):
if self.current_file_list_index < len(self.file_lists):
file_path = self.file_lists[self.current_file_list_index]
if os.path.exists(file_path):
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)}")
for client_ip in self.client_ips:
self.prepare_file_transfer(client_ip, file_path)
else:
print(f"檔案不存在: {file_path}")
else:
self.status_label.config(text="所有檔案已成功傳輸!")
print("所有檔案已成功傳輸!")
def prepare_file_transfer(self, client_ip, file_path):
print(f"準備傳輸檔案到 {client_ip}: {file_path}")
self.send_file_list(client_ip, file_path)
def send_file_list(self, client_ip, file_path):
try:
self.server_socket.sendto(file_path.encode(), (client_ip, 8500))
time.sleep(2)
selected_clients = len(self.client_ips)
file_size = os.path.getsize(file_path)
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {selected_clients} --nokbd --autostart 3"
result = subprocess.run(command, shell=True)
if result.returncode == 0:
self.status_label.config(text="檔案傳輸成功", fg="green")
self.current_file_list_index += 1
self.start_file_transfer() # 開始傳送下一個檔案
else:
self.status_label.config(text="檔案傳輸失敗", fg="red")
print("檔案傳輸失敗")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = Server(root)
root.mainloop()
檔案使用
,使用繁體中文,想新增四個按鈕,第一個按鈕,在畫面上要排列在"# 建立傳輸檔案按鈕"這個的左側,名稱為"選定clinet端",當伺服端的使用者選定已連線的clinet端電腦後,就綁定clinet端電腦的資料,第二個按鈕,名稱為"解除clinet端綁定",在畫面上要排列在"選定clinet端"這個按鈕的左側,當伺服端的使用者按下按鈕後,清除被選定的clinet端電腦資料,第三個按鈕,排列在"# 建立傳輸檔案按鈕"這個的右側,名稱為"選取多個檔案",
使用方式為,伺服端使用者按下按鈕後,先選定要傳送的檔案數量,依照選擇的檔案數量,轉成檔案清單,一個檔案轉成一個檔案清單,三個檔案就轉成三個清單,看使用者選擇的數量,這個檔案清單要獨立存放,
第四個按鈕,名稱為"開始派送",在畫面上要排列在"選取多個檔案"這個按鈕的右側,功能是將第三個按鈕"選取多個檔案"選定的檔案,一次送出一個檔案清單,資料從第三個按鈕"選取多個檔案"來,先選擇多個檔案清單中的第一個檔案清單,分別給第一個按鈕"選定clinet端"的各clinet端電,執行def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
"這個程式,通知clinet端電腦,接著通知伺服端電腦執行"# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
",從"if result.returncode == 0"這個回傳值,通知己完成第一個檔案的傳輸,再回到多個檔案清單中的第二個檔案清單,再次執行分別給第一個按鈕"選定clinet端"的各clinet端電,執行def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
"這個程式,通知clinet端電腦,接著通知伺服端電腦執行"# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
file_size = os.path.getsize(file_path) # 取得檔案大小
self.status_label.config(text=f"目前傳輸檔案:{os.path.basename(file_path)},大小:{file_size // (1024 * 1024)} MB")
# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd --autostart 3 "
subprocess.run(command, shell=True)
",從"if result.returncode == 0"這個回傳值,直到檔案清單都被選用後結束程式
import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk
import time
import os
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(pady=10)
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
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)
# 新增 "選定 client 端" 按鈕
self.select_client_button = tk.Button(self.root, text="選定 client 端", command=self.select_client)
self.select_client_button.pack(side=tk.LEFT, padx=5)
# 新增 "解除 client 端綁定" 按鈕
self.unselect_client_button = tk.Button(self.root, text="解除 client 端綁定", command=self.unselect_client)
self.unselect_client_button.pack(side=tk.LEFT, padx=5)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10, side=tk.LEFT)
# 新增 "選取多個檔案" 按鈕
self.select_multiple_files_button = tk.Button(self.root, text="選取多個檔案", command=self.select_multiple_files)
self.select_multiple_files_button.pack(side=tk.RIGHT, padx=5)
# 新增 "開始派送" 按鈕
self.start_dispatch_button = tk.Button(self.root, text="開始派送", command=self.start_dispatch)
self.start_dispatch_button.pack(side=tk.RIGHT, padx=5)
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
self.status_label = tk.Label(self.root, text="目前傳輸狀態:等待中", font=("Arial", 12), fg="black")
self.status_label.pack(pady=10)
# 新增備註區域,左右兩格
self.footer_frame = tk.Frame(self.root)
self.footer_frame.pack(pady=20, fill=tk.X)
# 左邊格 - 跑馬燈效果,文字顏色改為gold
self.marquee_text = "感謝ChatGPT 感謝UDPcast "
self.marquee_label = tk.Label(self.footer_frame, text=self.marquee_text, font=("Arial", 12), fg="gold")
self.marquee_label.pack(side=tk.LEFT, padx=10)
# 右邊格 - 固定文字,文字顏色改為gold
self.fixed_label = tk.Label(self.footer_frame, text="製作者:WCS 日期:2024.10.01", font=("Arial", 12), fg="gold")
self.fixed_label.pack(side=tk.RIGHT, padx=10)
# 啟動跑馬燈
self.move_text()
# 取得伺服端的本地 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):
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
print(f"伺服端 IP: {local_ip}")
return local_ip
def move_text(self):
# 跑馬燈效果,將字串每次往左移動一個字符
self.marquee_text = self.marquee_text[1:] + self.marquee_text[0]
self.marquee_label.config(text=self.marquee_text)
self.root.after(200, self.move_text) # 每200毫秒移動一次
def receive_clients(self):
while True:
try:
data, addr = self.server_socket.recvfrom(1024)
client_data = data.decode().split(',')
# 確認收到的資料是否完整
if len(client_data) == 3:
client_ip, client_mac, client_name = client_data
# 排除伺服端自己的 IP,不顯示在表格中
if client_ip != self.server_ip:
self.update_or_insert_client(client_ip, client_mac, client_name, "Online")
else:
print(f"接收到不完整的資料:{client_data}")
except Exception as e:
print(f"接收資料時發生錯誤: {e}")
def update_or_insert_client(self, ip, mac, name, status):
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, status))
else:
# 插入新行並保存該行的 ID
item_id = self.tree.insert("", "end", values=(ip, mac, name, status))
self.client_data_dict[ip] = item_id
self.client_data_dict[mac] = item_id
def broadcast_for_30_seconds(self):
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):
threading.Thread(target=self.broadcast_for_30_seconds, daemon=True).start()
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 更新狀態為正在準備傳送
self.status_label.config(text="目前傳輸狀態:正在準備傳送檔案...")
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def select_client(self):
# 此處定義如何選定 client 端並綁定資料
selected_items = self.tree.selection()
if selected_items:
self.selected_clients = [self.tree.item(item, 'values')[0] for item in selected_items]
print(f"已選定的客戶端: {self.selected_clients}")
def unselect_client(self):
# 清除選定的 client 資料
self.selected_clients = []
print("已解除所有選定的客戶端")
def select_multiple_files(self):
# 允許使用者選擇多個檔案
self.selected_files = filedialog.askopenfilenames(title="選取多個檔案")
if self.selected_files:
print(f"已選取的檔案: {self.selected_files}")
def start_dispatch(self):
# 開始派送檔案的邏輯
if hasattr(self, 'selected_clients') and hasattr(self, 'selected_files'):
for client in self.selected_clients:
for file in self.selected_files:
print(f"正在派送 {file} 給 {client}")
self.status_label.config(text="目前傳輸狀態:派送完成")
else:
print("請選擇客戶端與檔案後再派送")
if __name__ == "__main__":
root = tk.Tk()
server = Server(root)
root.mainloop()