我编写了一个简单的 python 程序来发送 UDP 数据报。
import socket
client_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
host_name = socket.gethostname()
host_ip = '1.2.3.4'
print(host_ip)
port = 8080
message = b'0' * 65500
client_socket.sendto(message,(host_ip,port))
当我运行此代码时,它成功发送了一个 UDP 数据包。以下是 wireshark 日志,
我的无线接口的 MTU 是 1500,这是我通过运行ip link
命令发现的。
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eno2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000
link/ether 58:11:22:82:c2:ec brd ff:ff:ff:ff:ff:ff
altname enp0s31f6
3: wlo1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DORMANT group default qlen 1000
link/ether f4:26:79:33:7c:96 brd ff:ff:ff:ff:ff:ff
altname wlp0s20f3
我读到不能发送大于接口 MTU 的数据包,那么 UDP 数据包是如何传输的呢?有人能向我解释一下这里发生了什么吗?
附言:我尝试将其发送到我拥有的服务器,并且也能够收到该消息。
答案1
IPv4 和 IPv6 都支持传输层以下的分段,尽管它并不总是能正常工作(某些网络没有返回正确的路径 MTU 指示,某些网络直接丢弃分段),但在这种情况下,操作系统知道其本地 MTU 并将相应地对数据包进行分段。
就您而言,数据包在 IP 层被分段,但操作系统使用硬件卸载,因此分段实际上由您的 NIC 处理,并且对数据包捕获不可见。(这可以提供更好的性能,因为操作系统不需要排队多个数据包并在内存中复制有效负载数据块。)
使用ethtool -k wlan0 | grep -v '\[fixed\]'
查看可以打开/关闭的卸载功能。最有可能的是“generic-segmentation-offload”或“gso”;您可以使用 暂时禁用它ethtool -K wlan0 gso off
。
此外,还有一个套接字选项,您可以通过 setsockopt() 设置它以选择退出碎片化;然后 send() 调用将返回数据包过大的错误代码。对于 IPv4,选项是“IP_MTU_DISCOVER”,对于 IPv6,选项是“IPV6_MTU_DISCOVER”。请参阅man 7 ip
。
(如果您的 Python 版本没有这些常量,请在 /usr/include 中查找实际值 - 它们应该10
用于 IP_MTU_DISCOVER 和23
IPV6_MTU_DISCOVER。例如,setsockopt(SOL_IP, 10, False)
。)
(顺便说一句,Wi-Fi 接口的潜在最大 MTU 约为 2304 - 它们默认为 1500,以保持与 1500 的标准以太网配置兼容。)