首页 > 技术文章 > Windows下用Python发送UDP广播消息踩坑记录

FrankOu 2022-05-12 23:20 原文

背景

做计算机网络实验,其中有一个任务是让本机向网段内其他主机发送UDP广播消息,再通过Wireshark捕获。

网络环境是:一个手机开热点当路由,我和我舍友的电脑连接到手机的热点上。

用MacOS的我决定通过Python来完成这个任务,在本机上一切都正常,舍友和我的系统都能收到来自本机的UDP广播消息,但是当代码在舍友的电脑上运行时,却没有收到广播消息。

但是,舍友的主机向我的主机单独发送UDP消息,我却能正常的接收到。

最初的广播代码:

import platform
from socket import *
network = '255.255.255.255' # 发送地址为<广播>
serverPort = 1222# 请自行选择
clientSocket = socket(AF_INET, SOCK_DGRAM) # 创建 UDP 套接字
clientSocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) # 允许广播地址发送和接收信息包

message = "BroadCast from host A" # 广播的信息(仅供参考,可自行设置)
clientSocket.sendto(message.encode(), (network, serverPort))
clientSocket.close()

解决

通过万能的互联网,找到一个相关的问题Python UDP broadcast address anomaly on Windows 10 - Stack Overflow

原因在于舍友的电脑上有多个网卡,当发送广播时,广播会从某个网卡发出,但未必是我们正在联网使用或监听的网卡,对于一个socket类型,我们需要用bind(tuple(addr,port))方法进行绑定,让广播消息从接入互联网的网卡发出。其中addr是网卡上本机的IP,port是进行广播的端口。

那么如何去确定addr呢?我的思路是向一个服务器发送UDP数据包,通过socket.gethostname()就能获得IP,随后进行绑定即可。

最终代码如下:

import platform
from socket import *
network = '255.255.255.255' # 发送地址为<广播>
serverPort = 1222# 请自行选择
clientSocket = socket(AF_INET, SOCK_DGRAM) # 创建 UDP 套接字
clientSocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) # 允许广播地址发送和接收信息包

# Windows系统因为特殊的原因要多一些代码
if platform.system() == 'Windows':
    with socket(AF_INET, SOCK_DGRAM) as c:
        c.connect(('114.114.114.114', 80))
        local_ip = c.getsockname()[0]
        clientSocket.bind((local_ip, serverPort))

message = "BroadCast from host A" # 广播的信息(仅供参考,可自行设置)
clientSocket.sendto(message.encode(), (network, serverPort))
clientSocket.close()

推荐阅读