Python UDP 五節點互傳實作:讓 5 台電腦自動交換 IP、MAC、學號與姓名
在學習「網際網路應用」時,我們常會接觸到 IP、MAC Address、Port、TCP 與 UDP, 但如果只是背誦名詞,很難真正理解這些技術如何運作。
這次我們直接使用 Python 建立一個 「UDP 五節點學生資料互傳系統」。 5 位同學各自使用一台電腦,每台電腦都是一個 Node(節點), 不需要另外架設中央 Server。
每位同學啟動程式後輸入自己的學號與姓名, Python 自動取得本機的 IP Address 與 MAC Address, 再利用 UDP Broadcast 將自己的資料傳送給其他同學。
一、這個實驗要做什麼?
假設教室中有 5 位同學:
| 節點 | 學號 | 姓名 | IP |
|---|---|---|---|
| Node 1 | 411001001 | 王小明 | 192.168.1.101 |
| Node 2 | 411001002 | 李小華 | 192.168.1.102 |
| Node 3 | 411001003 | 陳大文 | 192.168.1.103 |
| Node 4 | 411001004 | 林小美 | 192.168.1.104 |
| Node 5 | 411001005 | 張志明 | 192.168.1.105 |
每一台電腦都同時具有兩個角色:
- UDP Sender:傳送自己的資料。
- UDP Receiver:接收其他同學的資料。
UDP Broadcast
↕
┌──────────────┼──────────────┐
│ │ │
Node 1 ←────→ Node 2 ←────→ Node 3
↕ ↕ ↕
Node 4 ←──────────────────────→ Node 5
每台電腦:
UDP Sender + UDP Receiver
二、為什麼選 UDP?
UDP 是 User Datagram Protocol(使用者資料包協定)。 和 TCP 不同,UDP 在傳送資料之前不需要先建立連線。
可以先用一個簡單概念理解:
UDP:比較像直接寄出一張明信片,不先確認對方是否準備好。
UDP 具有簡單、快速、負擔較低的特色, 常見於即時通訊、影音串流、DNS、IoT 裝置通訊及區域網路設備探索等應用。
三、什麼是 UDP Broadcast?
如果我們已經知道某位同學的 IP,可以直接將 UDP 封包傳給他的 IP, 這種方式稱為 Unicast(單播)。
但這次實驗的目的,是希望 5 台電腦可以互相發現彼此, 所以使用 Broadcast(廣播)。
255.255.255.255
程式將資料送往廣播位址後,在網路環境允許廣播的情況下, 同一區域網路中監聽指定 UDP Port 的其他節點就有機會收到這份資料。
四、我們要交換哪些資料?
每位同學要交換四項基本資料:
- 學號 student_id
- 姓名 name
- IP Address
- MAC Address
例如王小明的資料可以表示成:
{
"student_id": "411001001",
"name": "王小明",
"ip": "192.168.1.101",
"mac": "34:12:98:AA:01:10"
}
這種資料格式就是 JSON。 JSON 非常適合用來交換結構化資料, 在 Web API、IoT、雲端服務與 App 開發中都很常見。
五、Python 完整程式
5 位同學都使用完全相同的程式, 不需要為 Node 1、Node 2 分別修改程式。
import socket
import threading
import json
import uuid
import time
PORT = 5005
# 儲存目前收到的節點資料
nodes = {}
# ==========================================
# 取得本機 IP
# ==========================================
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
except:
ip = "127.0.0.1"
finally:
s.close()
return ip
# ==========================================
# 取得 MAC Address
# ==========================================
def get_mac():
mac = uuid.getnode()
return ":".join(
f"{(mac >> i) & 0xff:02X}"
for i in range(40, -1, -8)
)
# ==========================================
# 顯示目前節點
# ==========================================
def show_nodes():
print("\n" + "=" * 70)
print("目前找到的 UDP 節點")
print("=" * 70)
print(
f"{'學號':<15}"
f"{'姓名':<12}"
f"{'IP':<18}"
f"{'MAC'}"
)
print("-" * 70)
for student_id, info in nodes.items():
print(
f"{student_id:<15}"
f"{info['name']:<12}"
f"{info['ip']:<18}"
f"{info['mac']}"
)
print("-" * 70)
print(f"目前共有 {len(nodes)} 個節點")
# ==========================================
# UDP Receiver
# ==========================================
def receiver():
sock = socket.socket(
socket.AF_INET,
socket.SOCK_DGRAM
)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_REUSEADDR,
1
)
sock.bind(("", PORT))
print(f"\nUDP Receiver 已啟動,Port = {PORT}")
while True:
data, address = sock.recvfrom(4096)
try:
student = json.loads(
data.decode("utf-8")
)
student_id = student["student_id"]
nodes[student_id] = student
print(
f"\n收到 {student['name']} "
f"({student_id}) 的資料"
)
print(
f"UDP 封包來源 IP:{address[0]}"
)
show_nodes()
except Exception as e:
print("接收資料錯誤:", e)
# ==========================================
# UDP Broadcast
# ==========================================
def broadcast(student):
sock = socket.socket(
socket.AF_INET,
socket.SOCK_DGRAM
)
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_BROADCAST,
1
)
data = json.dumps(
student,
ensure_ascii=False
).encode("utf-8")
sock.sendto(
data,
("255.255.255.255", PORT)
)
sock.close()
# ==========================================
# 主程式
# ==========================================
print("=" * 60)
print(" UDP 五節點學生資料互傳系統")
print("=" * 60)
student_id = input("請輸入學號:")
name = input("請輸入姓名:")
ip = get_ip()
mac = get_mac()
my_data = {
"student_id": student_id,
"name": name,
"ip": ip,
"mac": mac
}
# 將自己的資料放入節點表
nodes[student_id] = my_data
print("\n我的節點資料")
print("-" * 40)
print("學號:", student_id)
print("姓名:", name)
print("IP:", ip)
print("MAC:", mac)
# ==========================================
# 啟動 Receiver Thread
# ==========================================
thread = threading.Thread(
target=receiver,
daemon=True
)
thread.start()
time.sleep(1)
# ==========================================
# 操作選單
# ==========================================
while True:
print("\n")
print("========= UDP 操作選單 =========")
print("1. 廣播我的資料")
print("2. 查看目前5個節點")
print("3. 離開")
print("===============================")
choice = input("請選擇:")
if choice == "1":
broadcast(my_data)
print("\n已將我的資料廣播給其他同學!")
elif choice == "2":
show_nodes()
elif choice == "3":
print("程式結束")
break
else:
print("請輸入 1、2 或 3")
六、實驗操作步驟
步驟 1:確認 5 台電腦連接同一個區域網路
建議課堂實驗時,5 台電腦連接同一台 Wi-Fi AP 或網路分享器。
步驟 2:5 台電腦都執行程式
python udp_5nodes.py
步驟 3:輸入自己的學號與姓名
請輸入學號:411001001 請輸入姓名:王小明 我的節點資料 ---------------------------------------- 學號:411001001 姓名:王小明 IP:192.168.1.101 MAC:34:12:98:AA:01:10
步驟 4:選擇「1. 廣播我的資料」
========= UDP 操作選單 ========= 1. 廣播我的資料 2. 查看目前5個節點 3. 離開 =============================== 請選擇:1 已將我的資料廣播給其他同學!
五位同學依序廣播自己的資料後, 每一台電腦就可以逐漸建立自己的節點資料表。
七、觀察實驗結果
當 5 個節點都成功交換資料後,可以看到:
====================================================================== 目前找到的 UDP 節點 ====================================================================== 學號 姓名 IP MAC ---------------------------------------------------------------------- 411001001 王小明 192.168.1.101 34:12:98:AA:01:10 411001002 李小華 192.168.1.102 50:EB:F6:22:18:21 411001003 陳大文 192.168.1.103 10:62:E5:31:21:33 411001004 林小美 192.168.1.104 A4:BB:6D:11:23:44 411001005 張志明 192.168.1.105 D8:43:AE:12:35:55 ---------------------------------------------------------------------- 目前共有 5 個節點
此時並不是只有某一台 Server 擁有資料, 而是每一個 Node 都可以接收其他節點廣播的資訊。
八、程式中的五個重要觀念
1. IP Address
IP Address 用來識別 IP 網路中的通訊端點。 在本實驗中,每一台學生電腦都有自己的區域網路 IP。
2. MAC Address
MAC Address 是網路介面的鏈路層識別資訊。 本程式由各節點自行取得 MAC,再把它當作 JSON 資料的一部分透過 UDP 傳送。
因此要特別注意: MAC 並不是 UDP 自動幫我們從遠端電腦取得的。
3. Port
這次所有節點共同使用:
UDP Port 5005
IP 可以理解為找到哪一台主機,而 Port 則協助作業系統將資料交給對應的網路應用程式。
4. JSON
Python Dictionary:
my_data = {
"student_id": student_id,
"name": name,
"ip": ip,
"mac": mac
}
透過:
json.dumps()
將資料序列化,再編碼成位元組後透過 UDP 傳送。
5. Thread
每一台電腦既要等待接收資料,又要讓使用者操作選單。 如果只有單一執行流程,程式可能卡在:
sock.recvfrom(4096)
因此使用另一個 Thread 負責 UDP Receiver:
thread = threading.Thread(
target=receiver,
daemon=True
)
thread.start()
這樣主程式可以處理操作選單, Receiver Thread 則持續等待其他同學送來 UDP 封包。
九、封包到底怎麼走?
例如 Node 1 的王小明按下「廣播我的資料」, Python 先建立資料:
王小明 ↓ Python Dictionary ↓ JSON ↓ UTF-8 Bytes ↓ UDP ↓ IP Network ↓ Broadcast ↓ Node 2 / Node 3 / Node 4 / Node 5 ↓ JSON Decode ↓ 加入 nodes 資料表
這一連串流程,就是應用程式資料真正進入網路並在不同主機間交換的一個簡化實例。
十、為什麼有時候 5 台電腦收不到彼此?
如果程式沒有錯,但同學彼此收不到資料,可以優先檢查:
- 5 台電腦是否位於可互通的同一區域網路。
- Windows 防火牆是否允許 Python 的網路通訊。
- UDP Port 5005 是否被防火牆或其他安全機制阻擋。
- Wi-Fi AP 是否開啟 Client Isolation / AP Isolation。
- 校園無線網路是否禁止裝置之間直接互相通訊。
校園 Wi-Fi 為了資訊安全,有時會限制用戶端彼此通訊或廣播。 因此課堂進行這類區域網路實驗時,可以準備獨立的教學用 Wi-Fi AP/分享器, 並依學校網路政策操作。
十一、思考題
- UDP 與 TCP 最大的差異是什麼?
- 為什麼這個實驗適合使用 Broadcast?
- IP Address 和 MAC Address 的角色有什麼不同?
- 為什麼所有同學都使用 Port 5005?
- 為什麼程式需要使用 Thread?
- 如果同一位同學廣播 10 次,為什麼 nodes 不一定會增加成 10 筆?
- UDP 如果發生封包遺失,這支程式會知道嗎?
- 如果要指定只傳給 Node 3,應該繼續使用 Broadcast,還是改成 Unicast?
十二、進階挑戰:從 Broadcast 走向 Unicast
第一階段,我們利用 Broadcast 完成:
下一階段就可以利用已經取得的 IP Address, 改成指定對象傳送訊息。
Node 1 王小明
│
│ UDP Unicast
↓
Node 3 陳大文
訊息:
Hello Node 3!
如此一來,就能形成更完整的學習路徑:
↓
UDP Broadcast
↓
自動發現其他節點
↓
建立 IP / MAC / 學號 / 姓名資料表
↓
UDP Unicast
↓
指定同學互傳訊息
十三、結語
這個實驗看似只是讓 5 位同學互傳「學號、姓名、IP、MAC」, 實際上已經把許多網際網路應用的重要概念串在一起:
IP → MAC → Port → Socket → UDP → Broadcast → JSON → Thread → Peer Node
學習網路程式設計最好的方式之一,就是讓不同的實體電腦真正交換資料。 當 Node 1 按下傳送,而 Node 2、Node 3、Node 4、Node 5 的螢幕真的出現 Node 1 的資料時, 原本抽象的 UDP、IP、Port 與封包概念,就會變成可以觀察、測試與修改的程式實驗。
下一個實驗,我們可以在這個五節點架構上加入 「選擇同學 → UDP Unicast → 一對一傳送文字訊息」, 進一步製作一個簡易的區域網路聊天室。
