我想实验和学习UDP协议。所以我开始编写一个服务器和一个客户端发送一个.wav文件从客户端到服务器,并在我的耳机上输出,因为我收到样品。我遇到了一些非常奇怪的行为与下面的代码
客户
import socket
import wave
import time as ti
sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address2 = ('192.168.0.196',40000)
BUFFER_SIZE = 1024
wf = wave.open(r'path\song.wav','rb')
data = wf.readframes(BUFFER_SIZE)
print(len(data))
sent = sock.sendto(data,server_address2)
#response,addr = sock.recvfrom(1024)
while data!=b'':
data = wf.readframes(BUFFER_SIZE)
sent = sock.sendto(data,server_address2)
ti.sleep(0.04)
服务器
from scipy.io import wavfile
import socket
import pyaudio
import time
import struct
import wave
import numpy as np
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('192.168.0.196',40000)
print('starting up on %s port %s' %(server_address, str(40000)))
sock.bind(server_address)
# BUFFER_SIZE = 1
# #OUTPUT FILE OPTIONS
# # Opening audio file as binary data
wf = wave.open(r'path\song.wav', 'rb')
# # Instantiate PyAudio
p = pyaudio.PyAudio()
file_sw = wf.getsampwidth()
# print(file_sw)
print("channels: " ,wf.getnchannels())
print("sampwidth: ",wf.getsampwidth())
print("framerate: " ,wf.getframerate())
print("p.get_format_from_width(file_sw): ", p.get_format_from_width(file_sw))
televizor = 7
casti = 5
stream = p.open(format=p.get_format_from_width(file_sw),
channels=1,
rate=22050,
output_device_index=casti,
output=True
#stream_callback = callback
)
#sock.settimeout(0.3)
while True:
try:
data, addr = sock.recvfrom(2048*2*2)
stream.write(data)
print(data)
except KeyboardInterrupt:
break
wav的样本被传送到服务器上,一秒钟内听起来还不错,但随后速度加快,一首1分钟长的歌曲在2秒内就结束了。
即使我在客户机上写了time.sleep(任意数字),让它慢点发送这些样本,它也会变得越来越快。这种行为是什么?
发生的情况是,你的网络传输音频数据到服务器的速度比你服务器的音频卡播放音频数据的速度快得多。
正因为如此(再加上UDP没有任何流量控制的概念),你的服务器的传入UDP包缓冲区很快就满了(而你的服务器却在里面被阻塞了)。stream.write(data)
等待一个音频块回放),这时任何额外的UDP数据包都会被服务器的网络栈默默地丢弃(这是UDP的预期行为)。
如果你想接收大部分或所有的UDP数据包,你需要对你的服务器进行编码,使它总是调用 "UDP"。recvfrom()
及时,这样它就可以在缓冲区填满之前从套接字的receive-buffer中取出传入的UDP数据包。 (注意,即使这样也不能保证你能收到所有的数据,因为UDP包也可能因为其他原因而被丢弃)。
一种方法是在服务器上用单独的线程处理网络和音频播放,这样音频播放就不会耽误网络数据接收。 另外,你也可以通过使用 select()
或类似 (取决于PyAudio是如何实现的;我从来没有使用过那个API,所以我不能确定)。
或者,你可以减慢你的客户端的速度,使它以服务器消耗音频的速度发送音频(即每秒22050*4字节,或任何它的工作原理),尽管即使这样也只对短音频文件有帮助,因为最终音频将由于时钟漂移等原因在服务器端不足或超支。