非阻塞从HTTP流读取/记录
我有一个客户需要连接到一个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 个回答
另一种选择是直接使用 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
模块,它可以告诉你什么时候有数据可以读取。
嘿,这里有三个问题在一起呢!;-)
有时候可能会出现阻塞情况——即使你的服务器生成数据很快,网络瓶颈也可能导致读取时出现阻塞。
用“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
显然,我省略了错误检查和异常处理等内容,但希望这些信息能让你大致明白。