2009-04-22 24 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