伺服端-
說明
import socket
import threading
import tkinter as tk
from tkinter import messagebox,filedialog, ttk
import time
import subprocess
class Server:
def __init__(self, root):
self.root = root
self.root.title("Server")
self.root.geometry("800x600")
# 新增標題 "UDP派送檔案"
title_label = tk.Label(root, text="UDP派送檔案", font=("Arial", 18), fg="deep sky blue")
title_label.pack()
# 建立表格標題與資料置中
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)
# 監聽客戶端的進度更新
threading.Thread(target=self.listen_for_progress_updates, daemon=True).start()
# 在 UI 上建立進度顯示的 Label
self.progress_label = tk.Label(self.root, text="傳輸進度:等待中", font=("Arial", 14), fg="green")
self.progress_label.pack(pady=10)
# 進度更新區
self.progress_label = tk.Label(self.root, text="傳輸進度:", font=("Arial", 12), fg="green")
self.progress_label.pack()
# 新增備註區域,左右兩格
self.footer_frame = tk.Frame(self.root)
self.footer_frame.pack(pady=20, fill=tk.X)
# 左邊格 - 跑馬燈效果,文字顏色改為navy
self.marquee_text = "感謝ChatGPT 感謝UDPcast "
self.marquee_label = tk.Label(self.footer_frame, text=self.marquee_text, font=("Arial", 12), fg="navy")
self.marquee_label.pack(side=tk.LEFT, padx=10)
# 右邊格 - 固定文字,文字顏色改為navy
self.fixed_label = tk.Label(self.footer_frame, text="製作者:WCS 日期:2024.10.01", font=("Arial", 12), fg="navy")
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()
# 接收進度更新的線程
threading.Thread(target=self.listen_for_progress_updates, daemon=True).start()
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 get_local_ip(self):
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(',')
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")
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:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items]
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}")
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:
transfer_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
transfer_socket.sendto(file_list_str.encode(), (client_ip, 8500))
self.start_file_transfer(file_paths)
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def start_file_transfer(self, file_paths):
try:
for file_path in file_paths:
print(f"傳送檔案: {file_path}")
subprocess.run(['udp-sender.exe', '--file', file_path, '--port', '8500', '--nokbd'])
except Exception as e:
print(f"傳輸檔案時發生錯誤: {e}")
def listen_for_progress_updates(self):
progress_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
progress_socket.bind(("", 8501))
while True:
try:
data, addr = progress_socket.recvfrom(1024)
progress_message = data.decode()
print(f"來自 {addr} 的進度更新: {progress_message}")
self.update_progress(progress_message)
if "完成" in progress_message:
self.notify_transfer_complete()
except Exception as e:
print(f"接收進度更新時發生錯誤: {e}")
def update_progress(self, message):
self.progress_label.config(text=f"傳輸進度:{message}")
def notify_transfer_complete(self):
messagebox.showinfo("傳輸完成", "檔案已成功傳輸完畢!")
# 啟動伺服端程式
if __name__ == "__main__":
root = tk.Tk()
server_app = Server(root)
root.mainloop()
client端
說明
import socket
import threading
import time
import uuid
import os
import subprocess
import re
# 獲取 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()
# 發送客戶端的報到信息 (IP、MAC、電腦名稱)
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:
try:
report_msg = f"{socket.gethostbyname(socket.gethostname())},{get_mac_address()},{get_computer_name()}"
client_socket.sendto(report_msg.encode(), ('<broadcast>', 37020))
except Exception as e:
print(f"發送報到時發生錯誤: {e}")
time.sleep(5)
# 接收檔案清單並處理接收檔案
def receive_file_list():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.bind(("", 8500))
while True:
try:
data, addr = client_socket.recvfrom(1024)
file_list = data.decode().split(",")
print(f"接收到的檔案清單: {file_list}")
# 建立接收檔案的資料夾
os.makedirs("received_files", exist_ok=True)
# 使用 udp-receiver.exe 接收檔案並回報進度
for file_name in file_list:
save_path = os.path.join("received_files", os.path.basename(file_name))
receive_file_with_progress(save_path, addr[0])
except Exception as e:
print(f"接收檔案清單時發生錯誤: {e}")
def receive_file_with_progress(save_path, server_ip):
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 使用 Popen 來執行 udp-receiver.exe 並捕獲輸出
process = subprocess.Popen(
['udp-receiver.exe', '--file', save_path, '--port', '8500'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)
for line in process.stdout:
# 假設 udp-receiver.exe 的進度輸出包含 "% complete" 等資訊
print(f"接收進度: {line.strip()}") # 顯示在 client 端的控制台上
if re.search(r'(\d+)%', line):
# 如果捕捉到進度,將其回報給 server
client_socket.sendto(line.encode(), (server_ip, 8501))
# 檔案接收完成
if process.wait() == 0:
print("檔案接收完成")
client_socket.sendto("傳輸完成".encode(), (server_ip, 8501))
else:
print("檔案接收失敗")
client_socket.sendto("傳輸失敗".encode(), (server_ip, 8501))
# 啟動客戶端
if __name__ == "__main__":
threading.Thread(target=send_report, daemon=True).start()
threading.Thread(target=receive_file_list, daemon=True).start()
while True:
time.sleep(1)
伺服端,可行版
-傳輸時,可以顯示檔案名稱,完成後變成等待中
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")
# 新增標題
self.title_label = tk.Label(self.root, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue", anchor="center")
self.title_label.pack(pady=10) # 使用 pady 增加一些間距
# 建立表格
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(pady=10)
# 新增傳輸狀態欄位
self.transfer_status_label = tk.Label(self.root, text="等待中", font=("Arial", 12), fg="black")
self.transfer_status_label.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):
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(',')
# 確認收到的資料是否完整
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:
# 傳送檔案清單給每一個選中的客戶端
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}")
# 更新傳輸狀態顯示
total_size = sum(os.path.getsize(file) for file in file_paths) # 計算檔案總大小
self.update_transfer_status(f"傳輸中:{file_paths[0]} (大小: {total_size} bytes)")
# 傳送檔案清單給 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):
subprocess.run(['udp-sender.exe', '--file', file_path, '--port', '8500','--nokbd'])
# 傳輸完成,更新狀態
self.update_transfer_status("等待中")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
self.update_transfer_status("傳輸失敗")
def update_transfer_status(self, status):
# 更新傳輸狀態欄位的內容
self.transfer_status_label.config(text=status)
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()
伺服端-簡易版
-沒有標題
-選完直接傳送,不知傳輸結果
-client會自動報到
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")
# 建立表格
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)
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(self.root, text="傳送檔案", command=self.select_clients_and_file)
self.transfer_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):
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(',')
# 確認收到的資料是否完整
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_clients_and_file(self):
selected_items = self.tree.selection()
if selected_items:
clients = []
for item in selected_items:
item_values = self.tree.item(item, 'values')
client_ip = item_values[0]
clients.append(client_ip)
if clients:
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
self.prepare_file_transfer_list(clients, file_paths)
def prepare_file_transfer_list(self, clients, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {clients}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(clients, file_paths), daemon=True).start()
def send_file_list(self, clients, file_paths):
client_count = len(clients)
file_list_str = ",".join(file_paths)
try:
# 先傳送檔案清單到每個客戶端
for client_ip in clients:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 使用 udp-sender.exe 傳送檔案給所有選擇的 clients
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','--nokbd'])
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
# 啟動伺服端
root = tk.Tk()
server = Server(root)
root.mainloop()