[Twisted-Python] flushing data to clients after a global variable is updated in server

Jessica Tsui jesadjust at gmail.com
Thu Apr 30 21:32:41 MDT 2015


Hi Glyph,

  Thank you so much for your detailed explanation. You are a life saver!

Best,
Jessica

2015-05-01 3:39 GMT+08:00 Glyph Lefkowitz <glyph at twistedmatrix.com>:

>
> On Apr 29, 2015, at 12:33 AM, Jessica Tsui <jesadjust at gmail.com> wrote:
>
> Hi, it's me again. I am still working on the kivy app, and now I am trying
> to add a function to the app - the server side has two seconds to decide if
> he wants to alter the original data sent from one client to other clients.
> If he would like to do so, he has to press a button. If he does not press
> the button in 2 seconds, the original data will be sent to other clients
> automatically.
>
> Right now I am trying to achieve that with the following code - by calling
> the MultiClientEcho().dataReceived(msg, "censored") line in the function as
> if MultiClientEcho received another data, however once i click the button,
> the program crashed and said (MultiClientEcho().dataReceived(msg,
> "censored")
>  TypeError: __init__() takes exactly 3 arguments (1 given))
>
>
> The first problem here is that "MultiEchoClient()" means "create a *new* MultiEchoClient".
> Since MultiEchoClient.__init__ takes "factory" and "app" parameters, you
> tried to create it with 1 parameter (just "self", which is passed
> implicitly) instead of the required 3 (self, factory, app).
>
> The second problem, once you've addressed that, is that you almost
> certainly don't want to create a new MultiEchoClient :).  It's not clear to
> me *which* MultiEchoClient you are trying to send this message to.
>
> The third problem is that you should never call dataReceived yourself.
>  dataReceived is a method invoked by Twisted to tell your protocol that
> data has arrived.
>
> The fourth problem is that you're treating dataReceived as delivering a
> discrete message.  It doesn't; it delivers a segment of some data in the
> stream coming from a client.  This is our most popular FAQ; basically, you
> need to use a NetstringReceiver or something to ensure you're getting
> complete messages in dataReceived:
> https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Whyisprotocol.dataReceivedcalledwithonlypartofthedataIcalledtransport.writewith
>
> Hopefully once you've addressed all these it will work more like you want
> it to :).
>
> I wonder how can I fix this and achieve the function I am aiming at?
>
>
> I've put other comments inline in the code below.
>
> import kivy
> from kivy.app import App
> from kivy.uix.label import Label
> from kivy.uix.scatter import Scatter
> from kivy.uix.boxlayout import BoxLayout
> from kivy.uix.scrollview import ScrollView
> from kivy.uix.button import Button
> from kivy.graphics.vertex_instructions import Rectangle
> from kivy.graphics.context_instructions import Color
> from kivy.graphics.instructions import Instruction
> from kivy.base import runTouchApp
> from kivy.lang import Builder
> import socket
>
> You don't actually use "socket" anywhere in this module so you don't need
> to import it :).
>
> from kivy.core.window import Window
> import pygame
> import random
> from kivy.support import install_twisted_reactor
> install_twisted_reactor()
> from twisted.internet import protocol, defer
> from time import sleep
> from twisted.internet import reactor, task
> from twisted.protocols.basic import LineReceiver
> from twisted.internet.protocol import Protocol, Factory
>
> censored = 0
>
> class MultiClientEcho(protocol.Protocol):
>     def __init__(self, factory, app):
>         self.factory = factory
>         self.app = app
>
>     def connectionMade(self):
>         self.factory.clients.append(self)
>
>     def dataReceived(self, data):
>
>         storedmessage = self.factory.app.handle_message(data)
>
>         global censored
>
> Rather than making this a global variable, consider putting it (as with
> "app" and with "clients") onto the factory, so you can instantiate multiple
> MultiClientEchoFactory instances in one process.
>
>         def f(data):
>                 for client in self.factory.clients:
>
> This is a bit too much indentation - you should stick to 4 spaces per
> indent for stylistic reasons :).
>
>                     client.transport.write(data)
>                     print "this will run in 1 sec after it's scheduled: %s" % data
>
>         if censored == 0 and storedmessage:
>                 reactor.callLater(2, f, data)
>
> You're not hanging on to the result of this callLater call, which means
> you are giving up any way of stopping this call from happening in the
> future.  If you want to allow the caller to cancel it, note that
> reactor.callLater returns an IDelayedCall, which has a "cancel()" method
> that stops it from happening if it hasn't happened yet.  See the API
> documentation here;
> https://twistedmatrix.com/documents/15.1.0/api/twisted.internet.interfaces.IReactorTime.callLater.html
>
>
>                 # client.transport.write(data)
>         elif censored == 1:
>                 reactor.callLater(0, f, 'censored')
>                 censored == 0
>
> I think maybe you mean "censored = 0" here? "censored == 0" just means
> "compare censored to 0" which will create a True or False value but
> otherwise do nothing; in this context, it pretty much means "do nothing".
>
>
>     def connectionLost(self, reason):
>         self.factory.clients.remove(self)
>
>
> class MultiClientEchoFactory(protocol.Factory):
>     protocol = MultiClientEcho
>
>     def __init__(self, app):
>         self.clients = []
>         self.app = app
>
>     def buildProtocol(self, addr):
>         return MultiClientEcho(self, self.app)
>
>
> class ServerApp(App):
>     def build(self):
>         self.label = Label(text="server started\n")
>
>
>         self.approve_btn = Button(text="approve")
>         # self.approve_btn.bind(on_release=self.send_message)
>         self.banned_btn = Button(text="banned")
>         self.banned_btn.bind(on_release=self.banned_message)
>         self.layout = BoxLayout(orientation='vertical', spacing=10)
>
>         reactor.listenTCP(8000, MultiClientEchoFactory(self))
>
> Rather than calling listenTCP directly, you should be using some kind of
> Endpoint here; TCP4ServerEndpoint if you just want to hard code port 8000,
> or serverFromString if you want to allow your user to customize it.  See
> this document:
> https://twistedmatrix.com/documents/15.1.0/core/howto/endpoints.html
>
>         self.layout.add_widget(self.label)
>         self.layout.add_widget(self.banned_btn)
>
>         return self.layout
>
>     def banned_message(self, msg):
>         global censored
>         censored = 1
>         self.label.text += "censored\n"
>         MultiClientEcho().dataReceived(msg, "censored")
>         print censored
>         return censored
>
>     def handle_message(self, msg):
>         self.label.text += "%s\n" % msg
>         return msg
>
>
> if __name__ == '__main__':
>     ServerApp().run()
>
> for i in range(0,1):
>     print 1-i
>     sleep(0.1)
>
>
>
>
> Thanks again for using Twisted!
>
> -g
>
>
> _______________________________________________
> Twisted-Python mailing list
> Twisted-Python at twistedmatrix.com
> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20150501/6feb6813/attachment-0002.html>


More information about the Twisted-Python mailing list