Python UDP套接字recvfrom阻塞无数据问题(终端/C#可正常运行)
UDP音频流接收问题:Python
sock.recvfrom() 阻塞但Wireshark显示有数据包 问题背景
我有一台模拟转数字音频的设备,通过UDP协议推送音频流到本地服务器。使用终端命令echo 'subscribe' | nc -u 192.168.0.194 9444或C#代码都能正常接收约15秒的响应数据,但Python脚本调用sock.recvfrom()时始终阻塞超时,尽管Wireshark明确显示设备已向我的笔记本发送UDP数据包。需要找出问题原因并解决,最终实现音频播放。
可能的原因
- 套接字绑定地址错误:如果Python脚本绑定了
127.0.0.1,只能接收本地回环数据包,无法获取设备从局域网发来的流量 - 端口不匹配:Wireshark中显示的数据包目标端口,和Python脚本监听的端口不一致
- 套接字配置问题:未设置
SO_REUSEADDR/SO_REUSEPORT导致端口占用,或超时时间设置过短 - 网络权限限制:Python进程被防火墙拦截(即使nc和C#能正常接收,Python可能未被添加到防火墙允许列表)
- UDP连接特性忽略:UDP是无连接协议,设备可能将音频流发回订阅命令的源端口,如果Python用不同套接字发送订阅和接收数据,会导致接收不到
解决办法
- 绑定到全局地址:将套接字绑定到
0.0.0.0,确保能接收所有网卡的 incoming 流量 - 核对端口一致性:用Wireshark确认设备发送数据包的目标端口,确保Python监听的端口完全匹配
- 调整套接字配置:添加端口复用选项,先关闭超时设置测试接收
- 检查防火墙权限:临时关闭防火墙测试,或手动将Python.exe添加到防火墙允许列表
- 复用同一套接字:用同一个套接字发送订阅命令和接收音频流,确保设备能将数据发回正确的源端口
修正后的Python接收代码示例
import socket DEVICE_IP = "192.168.0.194" DEVICE_PORT = 9444 LOCAL_LISTEN_PORT = 9444 # 与Wireshark显示的目标端口一致 # 创建UDP套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 允许端口复用,避免端口占用问题 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定到所有网卡的指定端口 sock.bind(("0.0.0.0", LOCAL_LISTEN_PORT)) # 先注释超时设置,确认能接收数据后再调整 # sock.settimeout(15) # 发送订阅命令 sock.sendto(b"subscribe", (DEVICE_IP, DEVICE_PORT)) print("等待接收音频数据...") try: while True: data, addr = sock.recvfrom(4096) # 缓冲区大小根据音频包调整 print(f"来自{addr}的数据包,长度:{len(data)}") # 后续处理音频数据 except KeyboardInterrupt: print("手动终止接收") finally: sock.close()
音频播放实现(基于PyAudio)
假设设备输出的是16位单声道PCM音频,可通过pyaudio直接播放:
- 安装依赖:
pip install pyaudio - 整合播放逻辑的代码:
import socket import pyaudio # 设备配置 DEVICE_IP = "192.168.0.194" DEVICE_PORT = 9444 LOCAL_LISTEN_PORT = 9444 # 音频参数(需匹配设备输出格式,参考设备说明书) CHUNK = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 # 初始化UDP套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("0.0.0.0", LOCAL_LISTEN_PORT)) sock.sendto(b"subscribe", (DEVICE_IP, DEVICE_PORT)) # 初始化音频播放流 p = pyaudio.PyAudio() stream = p.open( format=FORMAT, channels=CHANNELS, rate=RATE, output=True, frames_per_buffer=CHUNK ) print("开始播放音频...") try: while True: data, _ = sock.recvfrom(CHUNK * 2) # 16位PCM每个样本占2字节 stream.write(data) except KeyboardInterrupt: print("停止播放") finally: stream.stop_stream() stream.close() p.terminate() sock.close()
内容的提问来源于stack exchange,提问作者Saamer
相关产品推荐
相关产品推荐

