測試
伺服端
import socket
import threading
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import tkinter.font as tkFont
import time
import os
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 創建一個 Frame 用來放置標題和 IP 顯示
header_frame = tk.Frame(self.root)
header_frame.pack(pady=10)
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(header_frame, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(side=tk.LEFT) # 將標題放在左側
# 取得伺服端的本地 IP
self.server_ip = self.get_local_ip()
# 顯示伺服器 IP 的 Label
self.ip_label = tk.Label(header_frame, text=f"伺服器 IP: {self.server_ip}", font=("Arial", 14), fg="blue")
self.ip_label.pack(side=tk.LEFT, padx=(10, 0)) # 在標題和 IP 標籤之間加點距離
# 建立表格
self.tree = ttk.Treeview(root, columns=("IP", "MAC", "Name", "Status"), show='headings')
font_style = tkFont.Font(family="黑體", size=14) # 設定字體為黑體、大小為14
# 設定表格標題與資料置中
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.tag_configure("centered", font=font_style)
self.tree.pack(fill=tk.BOTH, expand=True)
# 儲存客戶端資料的字典
self.client_data_dict = {}
# 創建一個 Frame 用來放置按鈕
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 建立廣播按鈕
self.broadcast_button = tk.Button(button_frame, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(side=tk.LEFT, padx=(0, 10)) # 在按鈕之間加點距離
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(button_frame, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(side=tk.LEFT) # 將按鈕放在左側
# 新增檔案傳輸狀態顯示欄位,初始化為 "等待中"
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秒以確保客戶端準備好接收檔案
time.sleep(2)
# 計算選中的客戶端數量
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 傳送每一個檔案
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")
# 建立命令
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
print(f"執行命令: {command}") # 除錯輸出
# 執行命令
result = subprocess.run(command, shell=True, capture_output=True, text=True)
print(f"命令輸出: {result.stdout}") # 除錯輸出
print(f"命令錯誤: {result.stderr}") # 除錯輸出
if result.returncode != 0:
raise Exception(f"命令執行失敗,返回碼: {result.returncode}")
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
messagebox.showinfo("傳輸完成", "檔案已完成傳輸!")
time.sleep(2)
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")
if __name__ == "__main__":
root = tk.Tk()
server_app = Server(root)
root.mainloop()
伺服端-檔案處理待測
使用繁體中文,在w11環境下,程式碼是可以正常執行,"
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 "
subprocess.run(command, shell=True)
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
messagebox.showinfo("傳輸完成", "檔案已完成傳輸!")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.status_label.config(text="傳輸失敗!")",
現在希望"# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")",使用者在選完檔案後,先判斷有幾個檔案,如果是一個檔案,就直接轉成檔案清單,接原程式不修改,如果是2個檔案,要將檔案轉成2個檔案清單,如果是3個,要將檔案轉成,個檔案清單,依此類推,接著將2個檔案轉成兩個檔案清單,先發送一個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行後,確定傳輸全部完成執行subprocess.run(command, shell=True)
,再開始進行第2個檔案清單的傳送,進行發送一個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行,請協助修正程式,謝謝
-測試2
使用繁體中文, 在w11環境下,程式碼是可以正常執行,,"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" # 執行命令並檢查返回值 result = subprocess.run(command, shell=True) if result.returncode == 0: self.status_label.config(text="目前傳輸狀態:檔案傳輸完成") else: self.status_label.config(text="傳輸失敗!") print(f"傳輸檔案 {file_path} 時發生錯誤,返回碼:{result.returncode}") # 所有檔案傳輸完成後的狀態更新 messagebox.showinfo("傳輸完成", "所有檔案已完成傳輸!") self.status_label.config(text="目前傳輸狀態:所有檔案傳輸完成!") except Exception as e: print(f"傳送檔案清單時發生錯誤: {e}") self.status_label.config(text="傳輸失敗!") ",
現在希望"# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")",使用者在選完檔案後,先判斷有幾個檔案,如果是一個檔案,就直接轉成檔案清單,接原程式不修改,如果是2個檔案,要將檔案轉成2個檔案清單,如果是3個,要將檔案轉成,個檔案清單,依此類推,接著將2個檔案轉成兩個檔案清單,先發送一個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行後, 當"if result.returncode == 0: "回傳是0後,才確定傳輸全部完成執行,
,接著再開始進行第2個檔案清單的傳送,進行發送一個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行,請協助修正程式,謝謝
使用繁體中文,在w11環境下,下列程式碼是可以正常執行,
"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 "
# 執行命令並檢查返回值
result = subprocess.run(command, shell=True)
if result.returncode == 0:
self.status_label.config(text="目前傳輸狀態:檔案傳輸完成")
else:
self.status_label.config(text="傳輸失敗!")
print(f"傳輸檔案 {file_path} 時發生錯誤,返回碼:{result.returncode}")
sys.exit(1) # 停止程式,返回非零值表示錯誤
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
messagebox.showinfo("傳輸完成", "檔案已完成傳輸!")
"現在希望"# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")",使用者在選完檔案後,先判斷有幾個檔案,如果是一個檔案,就直接轉成檔案清單,接原程式不修改,如果是2個檔案,要將檔案轉成2個檔案清單,如果是3個,要將檔案轉成,個檔案清單,依此類推,接著將2個檔案轉成兩個檔案清單,先發送一個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行後, 當"if result.returncode == 0: "回傳是0後,才確定傳輸全部完成執行,
,接著再開始進行剛剛第2個檔案清單的傳送,先發送一個第2個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行,請協助修正程式,謝謝
第四次
使用繁體中文,在w11環境下,下列程式碼是可以正常執行,
"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 "
# 執行命令並檢查返回值
result = subprocess.run(command, shell=True)
if result.returncode == 0:
self.status_label.config(text="目前傳輸狀態:檔案傳輸完成")
else:
self.status_label.config(text="傳輸失敗!")
print(f"傳輸檔案 {file_path} 時發生錯誤,返回碼:{result.returncode}")
sys.exit(1) # 停止程式,返回非零值表示錯誤
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
messagebox.showinfo("傳輸完成", "檔案已完成傳輸!")
"現在希望"# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")",使用者在選完檔案後,先判斷有幾個檔案,如果是1個檔案,就直接轉成檔案清單,接原程式不修改,如果是2個檔案,要將檔案轉成2個檔案清單,如果是3個,要將檔案轉成,個檔案清單,接著將2個檔案轉成2個檔案清單先暫存第2個檔案清單,發送第1個檔案清單給"for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
"這段程式,通知clinet端電腦,跟著執行" def prepare_file_transfer_list(self, client_ip, file_paths):
",而後等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行後, 當"if result.returncode == 0: "回傳是0後,才確定傳輸全部完成執行,
,接著再開始進行剛剛第2個檔案清單的傳送,先發送一個第2個檔案清單給"# 傳送檔案清單給 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):
",等"# 開始執行 udp-sender.exe
command = f"udp-sender.exe --file \"{file_path}\" --port 8500 --min-receivers {client_count} --nokbd "
subprocess.run(command, shell=True)"這段程式完成執行,請協助修正程式,謝謝