Windows下Python Socket实现SSDP搜索结果不稳定的问题求助
目标
我正在编写一个Python Windows应用程序,需要使用SSDP搜索可用的网络设备。搜索将用于获取特定设备类型的IP地址以建立连接。
问题
我们得到的SSDP搜索结果非常不一致,有时有效,有时失败。通过Wireshark和Device Sniffer(来自Meshcommander UPnP工具),我们发现当失败时,SSDP M-SEARCH会被发送到127.0.0.1,因此永远无法到达网络。它何时有效或失败似乎没有规律。我确信这是Windows特有的问题,因为相同的代码在Mac上可以正常工作。
间歇性工作的代码:
import socket import struct import time local_ip = '192.168.20.48' ssdp_addr = "239.255.255.250" ssdp_port = 1900 searchterm = 'ssdp:all' m_msg = f"""\ M-SEARCH * HTTP/1.1\r\n\ Host: {ssdp_addr}:{ssdp_port}\r\n\ Man: "ssdp:discover"\r\n\ ST: {searchterm}\r\n\ MX: 2\r\n\r\n""".encode("utf-8") if __name__ == "__main__": # Method 1, occassionally works. Most of the time sends to 127.0.0.1:port sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 5) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.settimeout(5) sock.sendto(m_msg, (ssdp_addr, ssdp_port)) # Sometimes works, sometimes doesn't... # If send worked, this will receive the response. But, if send is routed to 127.0.0.1, # nothing is received (which is expected given it is localhost) tLim = time.time() + 6 while time.time() < tLim: try: data, addr = sock.recvfrom(4096) print(f"Received from {addr}: {data}") except socket.timeout: break sock.close()
这个问题在PyPi上的ssdpy库中也会出现,这并不奇怪,因为该库的源代码与上述实现类似。
尝试的解决方案
我已经尝试了各种Windows配置(防火墙设置、服务、权限、切换网络等),但都无济于事。在网上搜索后,我找到了一种方法,发送的M-Search消息不再被路由到127.0.0.1,但我无法接收来自设备的响应!通过Wireshark,我可以看到设备发送了响应,但代码无法读取它。不过,这个实现会发现网络上同时发生的随机SSDP流量。
对应的代码:
import socket import struct import time local_ip = '192.168.20.48' ssdp_addr = "239.255.255.250" ssdp_port = 1900 searchterm = 'ssdp:all' m_msg = f"""\ M-SEARCH * HTTP/1.1\r\n\ Host: {ssdp_addr}:{ssdp_port}\r\n\ Man: "ssdp:discover"\r\n\ ST: {searchterm}\r\n\ MX: 2\r\n\r\n""".encode("utf-8") if __name__ == "__main__": # Method 2 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((local_ip, ssdp_port)) group = socket.inet_aton(ssdp_addr) mreq = struct.pack('4s4s', group, socket.inet_aton(local_ip)) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) sock.settimeout(5) ret = sock.sendto(m_msg, (ssdp_addr, ssdp_port)) # CODE to listen for SSDP responses. This finds unrelated network upnp notifies, but # does not get the response from the device. I can see the response from the device is # being sent with wireshark tLim = time.time() + 6 while time.time() < tLim: try: data, addr = sock.recvfrom(4096) print(f"Received from {addr}: {data}") except socket.timeout: break sock.close()
通过这个方法,Wireshark显示M-SEARCH的源/目标为192.168.20.48 / 239.255.255.250,设备的响应源/目标为192.168.30.82 / 192.168.20.48(其中192.168.20.48是PC本地IP,192.168.30.82是设备本地IP)。
求助
我想知道是否可以对这两种方法中的任何一种进行修改,使其能够持续正常工作。第一种方法有时有效,大多数时候无效;第二种方法始终能正确发送请求,但无法接收设备的响应。如果我的思路完全错误,我也愿意尝试全新的方法。
备注:内容来源于stack exchange,提问作者Tyler B

