非阻塞从HTTP流读取/记录

6 投票
4 回答
7444 浏览
提问于 2025-04-15 15:01

我有一个客户需要连接到一个HTTP流,并记录它接收到的文本数据。

我向流媒体服务器发送了一个HTTP GET请求……服务器回复后,会不断地发布数据……它会定期发布文本,或者发送一个“ping”(文本)消息……而且连接永远不会关闭。

我需要以非阻塞的方式读取和记录它接收到的数据。

我正在做类似这样的事情:

import urllib2

req = urllib2.urlopen(url)    
for dat in req: 
    with open('out.txt', 'a') as f:        
        f.write(dat) 

我有几个问题:
在流持续的情况下,这会不会阻塞?
每次读取的数据量是多少,能否指定或调整?
这是读取和记录HTTP流的最佳方法吗?

4 个回答

2

另一种选择是直接使用 socket 模块。你可以先建立一个连接,发送HTTP请求,然后把socket设置为非阻塞模式,这样就可以用 socket.recv() 来读取数据。在这个过程中,你需要处理一些异常,比如“资源暂时不可用”,这意味着没有数据可以读取。下面是一个非常简单的例子:

import socket, time

BUFSIZE = 1024

s = socket.socket()
s.connect(('localhost', 1234))
s.send('GET /path HTTP/1.0\n\n')
s.setblocking(False)

running = True

while running:
    try:
        print "Attempting to read from socket..."
        while True:
            data = s.recv(BUFSIZE)
            if len(data) == 0:      # remote end closed
                print "Remote end closed"
                running = False
                break
            print "Received %d bytes: %r" % (len(data), data)
    except socket.error, e:
        if e[0] != 11:      # Resource temporarily unavailable
            print e
            raise

    # perform other program tasks
    print "Sleeping..."
    time.sleep(1)

不过,如果你需要处理网页服务器的重定向,或者需要基于URL的基本认证等情况,使用 urllib.urlopen() 会有一些好处。此外,你还可以利用 select 模块,它可以告诉你什么时候有数据可以读取。

6

嘿,这里有三个问题在一起呢!;-)

有时候可能会出现阻塞情况——即使你的服务器生成数据很快,网络瓶颈也可能导致读取时出现阻塞。

用“for dat in req”来读取URL数据意味着你是逐行读取的——如果你在读取像图片这样的二进制数据,这样做就没什么用处。你可以使用

chunk = req.read(size)

这样可以更好地控制读取过程,当然也可能会出现阻塞。

是否是最佳方法取决于你问题中的具体情况。如果你需要完全不阻塞地运行,那你就需要考虑像Twisted这样的框架。如果你不想让阻塞影响你的进程,也不想使用Twisted(因为它和传统的阻塞方式完全不同),那么你可以开启一个线程来处理读取和写入文件的工作,而你的主线程可以继续执行其他任务:

def func(req):
    #code the read from URL stream and write to file here

...

t = threading.Thread(target=func)
t.start() # will execute func in a separate thread
...
t.join() # will wait for spawned thread to die

显然,我省略了错误检查和异常处理等内容,但希望这些信息能让你大致明白。

3

你现在使用的接口层级太高,导致你对一些问题,比如阻塞和缓冲区大小,控制得不够好。如果你不想完全使用异步接口(在这种情况下,之前提到的twisted是个不错的选择!),那为什么不试试httplib呢?毕竟它是标准库的一部分。HTTPResponse实例的.read(amount)方法在读取amount字节时,更可能不会阻塞太久,而不是通过urlopen返回的对象上的类似方法(虽然确实在这两个模块上没有相关的文档说明,嗯……)。

撰写回答