金亚洲技术笔记

凡是过往,皆为序章。

Mininet使用笔记(三)UDP通讯

Posted on   » 网络通信 • 610 words • 2 minute read
Tags: mininet, udp

一、准备

# 查看拓扑
mininet> dump
<Host h1: h1-eth0:10.0.0.1 pid=2390682> 
<Host h2: h2-eth0:10.0.0.2 pid=2390684> 
<OVSBridge s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None pid=2390689>

UDP 服务端(udp_server.py)

import socket

HOST = "0.0.0.0"
PORT = 8888

srv_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
srv_sock.bind((HOST, PORT))
print(f"UDP Server listen on {HOST}:{PORT}")

while True:
    data, addr = srv_sock.recvfrom(1024)
    print(f"Recv from {addr}: {data.decode('utf-8')}")
    srv_sock.sendto(b"Server ACK:" + data, addr)

UDP 客户端(udp_client.py)

import socket

SERVER_IP = "10.0.0.1"
SERVER_PORT = 8888

cli_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

msg = b"Hello UDP Test"
cli_sock.sendto(msg, (SERVER_IP, SERVER_PORT))

resp, addr = cli_sock.recvfrom(1024)
print(f"Recv reply: {resp.decode()}")

cli_sock.close()

二、正常 UDP 通讯抓包

# 终端1:h1 运行 UDP SERVER
mininet> h1 python3 udp_server.py
UDP Server listen on 0.0.0.0:8888
Recv from ('10.0.0.2', 42837): Hello UDP Test

# 终端2:h2 运行 UDP CLIENT
root@null:/home/null# sudo mnexec -a 2390684 python3 udp_client.py
Recv reply: Server ACK:Hello UDP Test

# 终端3:h2 抓包 UDP 通讯
root@null:/home/null# sudo mnexec -a 2390684 tcpdump -l -i h2-eth0 udp port 8888
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on h2-eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:21:05.673793 IP 10.0.0.2.39782 > 10.0.0.1.8888: UDP, length 14
13:21:05.674060 IP 10.0.0.1.8888 > 10.0.0.2.39782: UDP, length 25

三、模拟 UDP 客户端数据丢包

在客户端端 h2 添加 INPUT 过滤规则

mininet> h2 iptables -A INPUT -s 10.0.0.1 -p udp --sport 8888 -j DROP
# 终端1:h1 运行 UDP SERVER
mininet> h1 python3 udp_server.py
UDP Server listen on 0.0.0.0:8888
Recv from ('10.0.0.2', 42837): Hello UDP Test

# 终端2:h2 运行 UDP CLIENT
root@null:/home/null# sudo mnexec -a 2390684 python3 udp_client.py
# [无返回ACK数据]

# 终端3:h2 抓包 UDP 8888 端口报文
root@null:/home/null# sudo mnexec -a 2390684 tcpdump -l -i h2-eth0 udp port 8888
[sudo] password for null: 
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on h2-eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
14:23:19.959552 IP 10.0.0.2.42837 > 10.0.0.1.8888: UDP, length 14
14:23:19.959891 IP 10.0.0.1.8888 > 10.0.0.2.42837: UDP, length 25

四、总结

从抓包结果就可以看出,UDP 通讯很简单:没有握手、挥手报文,没有重试,也就是说 UDP 没有重传、超时、指数退避机制,发送即忘 (Send‑and‑pray)。如果想实现「超时 + 指数退避重试」,只能在应用层代码里实现。示例:

import socket
import time

SERVER_IP = "10.0.0.1"
SERVER_PORT = 8888

cli_sock =_INET, socket.SOCK_INET, socket.SOCK_DGRAM)
# 设置超时时间
base_timeout = 1
max_retry = 4
retry_count = 0

msg = b"Hello UDP Test"

while retry_count <= max_retry:
    cli_sock.sendto(msg, (SERVER_IP, SERVER_PORT))
    print(f"Send msg: {retry_count+1} times")
    # 指数退避:1s -> 2s -> 4s -> 8s
    timeout = base_timeout * (2 ** retry_count)
    cli_sock.settimeout(timeout)
    try:
        resp, addr = cli_sock.recvfrom(1024)
        print(f"Recv reply: {resp.decode()}")
        break
    except socket.timeout:
        retry_count += 1
        print(f"Timeout {timeout}s, retry...")

if retry_count > max_retry:
    print("Max retry reached, communication failed")

cli_sock.close()
×