Python发送HTTP响应

4 投票
1 回答
6789 浏览
提问于 2025-04-16 15:17

我正在尝试写一个非常简单的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请求,整个过程就像预期的那样——视频会被流式传输到客户端的套接字,并且被追加到新文件blah.mp4中。

但是,如果我创建一个虚拟的HTML页面,里面有<a href="http://localhost:8080/blah/mp4">download</a>的链接,然后尝试在这个链接上选择“另存为...”,get_video程序会被调用两次。第二次是在视频实际发送到客户端的套接字时。这里面肯定有某种缓冲机制在起作用。

注意send_video()中的try/except块。我之前在使用虚拟HTML页面的方法时遇到了“broken pipe”(断开的管道)错误,这表明客户端的套接字在写入时并不存在。我把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却报告了虚拟HTML链接方法的两个GET请求。我在这里没有看到明显的问题...

1 个回答

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()

谢谢。

撰写回答