代码之家  ›  专栏  ›  技术社区  ›  Corey Goldberg

来自HTTP流的非阻塞读/日志

  •  6
  • Corey Goldberg  · 技术社区  · 15 年前

    我有一个客户机,它连接到一个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 回复  |  直到 15 年前
        1
  •  3
  •   Alex Martelli    15 年前

    您使用的是高级接口,无法很好地控制诸如阻塞和缓冲块大小之类的问题。如果你不愿意一直走到异步接口(在这种情况下 twisted 已经有人建议,很难打败!)为什么不 httplib ,哪个在标准库中?httpResponse实例 .read(amount) 方法的阻塞时间比读取所需的时间长 amount 字节,而不是返回的对象上的类似方法 urlopen (尽管无可否认,两个模块上都没有关于这一点的文件化规范,嗯……)。

        2
  •  6
  •   Vinay Sajip    15 年前

    嘿,三个问题合一!;-)

    它有时会阻塞——即使您的服务器生成数据非常快,理论上网络瓶颈也会导致您的读取阻塞。

    使用“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
  •  3
  •   mhawke    15 年前

    另一种选择是使用 socket 模块直接。建立连接,发送HTTP请求,将套接字设置为非阻塞模式,然后使用 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)
    

    然而, urllib.urlopen() 有一些好处,如果Web服务器重定向,您需要基于URL的基本身份验证等。您可以利用 select 当有数据需要读取时,模块会告诉您。

        4
  •  1
  •   John La Rooy    15 年前

    是,当您赶上服务器时,它将阻塞,直到服务器产生更多数据。

    每个DAT将是一行,包括末尾的换行符。

    扭曲是个不错的选择

    我将在您的示例中交换“with”和“for”的值,您真的想打开和关闭到达的每一行的文件吗?