2009-04-22 23 views
9

我制作了一个简单的http服务器,使用Twisted发送Content-Type:multipart/x-mixed-replace标头。我正在使用它来测试我想设置为接受长期流的http客户端。使用Twisted的twisted.web类,我该如何刷新输出缓冲区?

出现的问题是我的客户端请求挂起,直到调用self.finish(),然后它一次接收所有多部分文档。

有没有办法手动将输出缓冲区刷新到客户端?我假设这就是为什么我没有收到个别多部分文件。

#!/usr/bin/env python 

import time 

from twisted.web import http 
from twisted.internet import protocol 

class StreamHandler(http.Request): 
    BOUNDARY = 'BOUNDARY' 

    def writeBoundary(self): 
     self.write("--%s\n" % (self.BOUNDARY)) 

    def writeStop(self): 
     self.write("--%s--\n" % (self.BOUNDARY)) 

    def process(self): 
     self.setHeader('Connection', 'Keep-Alive') 
     self.setHeader('Content-Type', "multipart/x-mixed-replace;boundary=%s" % (self.BOUNDARY)) 

     self.writeBoundary() 

     self.write("Content-Type: text/html\n") 
     s = "<html>foo</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 
     time.sleep(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>bar</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 
     time.sleep(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>baz</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 

     self.writeStop() 

     self.finish() 

class StreamProtocol(http.HTTPChannel): 
    requestFactory = StreamHandler 

class StreamFactory(http.HTTPFactory): 
    protocol = StreamProtocol 


if __name__ == '__main__': 
    from twisted.internet import reactor 
    reactor.listenTCP(8800, StreamFactory()) 
    reactor.run() 

回答

9

使用time.sleep()可防止扭曲工作。为了使它工作,你不能使用time.sleep(),你必须返回控制扭曲。修改现有的代码,这样做最简单的方法是使用twisted.internet.defer.inlineCallbacks,这是自切片面包退而求其次:

#!/usr/bin/env python 

import time 

from twisted.web import http 
from twisted.internet import protocol 
from twisted.internet import reactor 
from twisted.internet import defer 

def wait(seconds, result=None): 
    """Returns a deferred that will be fired later""" 
    d = defer.Deferred() 
    reactor.callLater(seconds, d.callback, result) 
    return d 

class StreamHandler(http.Request): 
    BOUNDARY = 'BOUNDARY' 

    def writeBoundary(self): 
     self.write("--%s\n" % (self.BOUNDARY)) 

    def writeStop(self): 
     self.write("--%s--\n" % (self.BOUNDARY)) 

    @defer.inlineCallbacks 
    def process(self): 
     self.setHeader('Connection', 'Keep-Alive') 
     self.setHeader('Content-Type', "multipart/x-mixed-replace;boundary=%s" % (self.BOUNDARY)) 

     self.writeBoundary() 

     self.write("Content-Type: text/html\n") 
     s = "<html>foo</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 


     yield wait(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>bar</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 
     self.writeBoundary() 

     yield wait(2) 

     self.write("Content-Type: text/html\n") 
     s = "<html>baz</html>\n" 
     self.write("Content-Length: %s\n\n" % (len(s))) 
     self.write(s) 

     self.writeStop() 

     self.finish() 


class StreamProtocol(http.HTTPChannel): 
    requestFactory = StreamHandler 

class StreamFactory(http.HTTPFactory): 
    protocol = StreamProtocol 


if __name__ == '__main__': 
    reactor.listenTCP(8800, StreamFactory()) 
    reactor.run() 

在Firefox的作​​品,我想它正确地回答你的问题。

1

原因似乎在FAQ for twisted中解释。扭曲的服务器实际上不会将任何内容写入下划线连接,直到反应器线程可以自由运行为止,在这种情况下在方法结束时。但是,在每次睡眠之前,您都可以使用reactor.doSelect(timeout)以使反应堆写入连接的内容。

+5

你不应该调用reactor.doSelect。这不能通过反应堆进行移动,它很容易在不希望重新进入的情况下重新进入反应堆而破坏反应堆。 – 2010-01-27 15:46:44

+2

尽管上面有关于doSelect的评论/纠正,但对于任何试图弄清楚其传输代码正在发生的事情的人来说,指向常见问题解答的指针是特别的 - 特别是“Twisted只会在您放弃对反应堆的执行控制后才发送数据。例如,如果你有一个无限循环将数据写入传输,数据永远不会被发送,因为控制永远不会离开你的代码并返回到反应堆。“ – Mick 2010-06-25 09:53:01