2009-04-22 6 views
9

He creado un servidor http simple utilizando Twisted, que envía el encabezado Content-Type: multipart/x-mixed-replace. Estoy usando esto para probar un cliente http que quiero configurar para aceptar una transmisión a largo plazo.Utilizando las clases twisted.web de Twisted, ¿cómo puedo eliminar mis buffers salientes?

El problema que ha surgido es que la solicitud de mi cliente se cuelga hasta que http.Request llama a self.finish(), luego recibe todos los documentos de varias partes a la vez.

¿Hay alguna forma de purgar manualmente los búferes de salida hasta el cliente? Supongo que es por eso que no recibo los documentos individuales de varias partes.

#!/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() 

Respuesta

9

El uso de time.sleep() evita que se tuerza al hacer su trabajo. Para que funcione no puede usar time.sleep(), debe devolver el control a retorcido en su lugar. La forma más fácil de modificar el código existente de hacerlo es mediante el uso de twisted.internet.defer.inlineCallbacks, que es la siguiente mejor cosa desde el pan de molde:

#!/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() 

que funciona en Firefox, supongo que responda a su pregunta correctamente.

+0

¿Esto permite efectivamente ejecutar cualquier otro código (como se selecciona por reactor) cuando se produce? Si es así, debe tener mucho cuidado de que los datos que está utilizando no se sobrescriban o alteren con el otro código. Vea el comentario en la URL a continuación que no he visto refutado en ninguna parte: http://twistedmatrix.com/pipermail/twisted-python/2007-February/014869.html – Mick

1

El motivo parece explicarse en el FAQ for twisted. El servidor retorcido en realidad no escribe nada en la conexión subrayada hasta que la rosca del reactor se ejecute libremente, en este caso al final de su método. Sin embargo, puede usar reactor.doSelect(timeout) antes de cada uno de sus sueños para que el reactor escriba lo que tiene a la conexión.

+5

Nunca debe llamar a reactor.doSelect. Esto no es portátil entre los reactores, y podría romper fácilmente el reactor volviendo a ingresar donde no se espera volver a entrar. –

+2

A pesar del comentario/corrección alrededor de doSelect anterior, para cualquiera que intente averiguar qué está sucediendo con su código de transporte, el puntero a las preguntas frecuentes es acertado; en particular, "Twisted puede enviar datos solo después de ceder el control de ejecución al reactor Por ejemplo, si tiene un bucle infinito escribiendo datos en un transporte, los datos nunca se enviarán, ya que el control nunca abandonará su código y regresará al reactor ". – Mick

Cuestiones relacionadas