[Twisted-Python] flushing data to clients after a global variable is updated in server
![](https://secure.gravatar.com/avatar/1e7c4a813cf986148b869f82fc97f395.jpg?s=120&d=mm&r=g)
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)) I wonder how can I fix this and achieve the function I am aiming at? 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 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 def f(data): for client in self.factory.clients: 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) # client.transport.write(data) elif censored == 1: reactor.callLater(0, f, 'censored') censored == 0 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)) 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)
![](https://secure.gravatar.com/avatar/e1554622707bedd9202884900430b838.jpg?s=120&d=mm&r=g)
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.d... <https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Whyisprotocol.d...> 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.
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 :).
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.I... <https://twistedmatrix.com/documents/15.1.0/api/twisted.internet.interfaces.I...>
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".
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 <https://twistedmatrix.com/documents/15.1.0/core/howto/endpoints.html> self.layout.add_widget(self.label)
Thanks again for using Twisted! -g
![](https://secure.gravatar.com/avatar/e1554622707bedd9202884900430b838.jpg?s=120&d=mm&r=g)
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.d... <https://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Whyisprotocol.d...> 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.
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 :).
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.I... <https://twistedmatrix.com/documents/15.1.0/api/twisted.internet.interfaces.I...>
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".
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 <https://twistedmatrix.com/documents/15.1.0/core/howto/endpoints.html> self.layout.add_widget(self.label)
Thanks again for using Twisted! -g
participants (2)
-
Glyph Lefkowitz
-
Jessica Tsui