我正在尝试编写一个非常简单的HTTP服务器,该服务器发送一个流式服务器端的视频。当客户端连接时,get_video程序(虚构)在另一个进程中启动,其stdout正在通过管道传送给我们(假设get_video将视频发送/流式传输到stdout)。我正在使用subprocess.Popen()
。
import subprocess, socket
def send_video(sock, programme_id):
p = subprocess.Popen(["get_video","--pid",programme_id], stdout=subprocess.PIPE)
sock.send("HTTP/1.1 200 OK\nContent-type: application/octet-stream\n\n")
while True:
chunk = p.stdout.read(1024)
if chunk:
try:
sock.send(chunk)
except Exception:
pass
else:
break
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('',8080))
s.listen(5)
while True:
client, address = s.accept()
data = client.recv(1024)
send_video(client, "123456")
client.close()
if __name__ == "__main__":
main()
这几乎无效。如果我使用wget http://localhost:8080/blah.mp4
发出HTTP请求,则整个事情按预期进行 - 视频将流式传输到客户端的套接字并附加到新文件blah.mp4。
但是,如果我创建一个带有<a href="http://localhost:8080/blah/mp4">download</a>
的虚拟HTML页面,然后在该链接上尝试“将目标/链接保存为...”,则会调用get_video程序两次。第二次是视频实际发送到客户端的套接字。必须有某种缓冲。
注意try/except
中的send_video()
阻止。我之前遇到了“破管”错误(使用虚拟HTML页面方法),表明客户端的套接字不在那里写入。我把try/except
放在那里尝试忽略它。
我很困惑。 HTTP请求看起来是一样的,我不确定我的浏览器(Firefox)做什么不同导致这种情况。
有什么想法吗?
修改
来自wget的标题:
GET /blah.mp4 HTTP/1.0
User-Agent: Wget/1.12 (linux-gnu)
Accept: */*
Host: 192.168.1.2:8080
Connection: Keep-Alive
来自html虚拟页面的标题:
GET /blah.mp4 HTTP/1.1
Host: 192.168.1.2:8080
User-Agent: Mozilla/5.0 (blah) Gecko/blah Firefox/3.6.16
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
python服务器只报告了其中一个,但Wireshark报告了2个针对虚拟html链接方法的GET请求。我在这里看不到任何明显的东西......
答案 0 :(得分:0)
我突然意识到,因为浏览器发送了一个GET后跟一个FIN,在点击链接后点击'保存目标为...'之后的ACK,那么那将是造成管道错误的原因。当您选择要保存文件的位置时,在浏览器中单击“确定”按钮,即发出另一个GET。发生了什么事情,一旦我检测到破裂的管道,我仍然卡在while True
循环中。所以我继续从Popen
对象读取并且每次都无法将其发送到客户端。一旦完成,下一个GET就能够继续,然后转移成功。呼。
简而言之,工作代码:
import subprocess, socket
def send_video(sock, programme_id):
p = subprocess.Popen(["get_video","--pid",programme_id], stdout=subprocess.PIPE)
sock.send("HTTP/1.1 200 OK\nContent-type: application/octet-stream\n\n")
while True:
chunk = p.stdout.read(1024)
if chunk:
try:
sock.send(chunk)
except socket.error, e:
sock.close()
break
else:
break
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('',8080))
s.listen(5)
while True:
client, address = s.accept()
data = client.recv(1024)
send_video(client, "123456")
client.close()
if __name__ == "__main__":
main()
感谢。