python 发送http响应
我正在尝试编写一个非常简单的HTTP服务器,它发送正在服务器端流式传输的视频。当客户端连接时, get_video 程序(虚构)在另一个进程中启动,并且其标准输出通过管道传输给我们(假设 get_video 将视频发送/流式传输到标准输出)。我为此使用 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 请求/code> 整个事情按预期进行 - 视频被传输到客户端的套接字并附加到新文件 blah.mp4。
但是,如果我使用 download
创建一个虚拟 HTML 页面,然后尝试“保存目标/链接” as...' 在该链接上, get_video 程序被调用两次。第二次是视频实际发送到客户端的套接字时。一定有某种缓冲正在进行。
请注意 send_video()
中的 try/ except
块。我之前遇到过“管道损坏”错误(使用虚拟 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 请求。我在这里没有看到任何明显的东西......
I am trying to write a very simple HTTP server which sends a video being streamed server side. When a client connects, the get_video program (fictional) is started in another process and its stdout is being piped to us (under the assumption get_video sends/streams video to stdout). I am using subprocess.Popen()
for this.
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()
This barely works. If I issue an HTTP request using wget http://localhost:8080/blah.mp4
the whole thing does as expected - the video is streamed to the client's socket and appended to the new file, blah.mp4.
However, if I create a dummy HTML page with <a href="http://localhost:8080/blah/mp4">download</a>
and then try 'save target/link as...' on that link, the get_video program is invoked twice. The second time being when the video is actually sent to the client's socket. There must be some sort of buffering going on.
Notice the try/except
block in send_video()
. I was getting a 'broken pipe' error before (with the dummy HTML page method), indicating the client's socket wasn't there to be written to. I put the try/except
there to try and ignore it.
I am fairly confused. The HTTP requests look the same, I'm not sure what my browser (Firefox) is doing differently to cause this.
Any ideas?
Edit:
The header from wget:
GET /blah.mp4 HTTP/1.0
User-Agent: Wget/1.12 (linux-gnu)
Accept: */*
Host: 192.168.1.2:8080
Connection: Keep-Alive
The header from html dummy page:
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
The python server reported only one of each, but Wireshark reported 2 GET requests for the dummy html link method. I don't see anything obvious here...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我突然意识到,因为在链接上单击“目标另存为...”后,浏览器发送了一个 GET,然后发送了一个 FIN,ACK,那么这就是导致管道损坏错误的原因。当您选择要保存文件的位置并在浏览器中单击“确定”按钮时,会发出另一个 GET。发生的事情是,一旦我检测到损坏的管道,我仍然陷入
while True
循环中。因此,我继续从Popen
对象中读取数据,但每次都无法将其发送到客户端。一旦完成,下一个 GET 就可以继续,传输就成功了。唷。简而言之,工作代码:
谢谢。
It dawned on me that, because the browser sends a GET followed by a FIN,ACK after the 'save target as...' has been clicked on the link, then that'll be what is causing the broken pipe error. It is when you choose where you want to save the file and hit 'OK' button in the browser that another GET is issued. What was happening was I was still stuck in that
while True
loop once I had detected the broken pipe. So I was continuing to read from thePopen
object and failing to send it to the client every time. Once this finished, the next GET was able to continue and the transfer was then successful. Phew.In short, the working code:
Thanks.