
I've got a simple little chat server set up that people can telnet into. The code is as follows: ---------- from twisted.internet import reactor from twisted.internet.protocol import ServerFactory from twisted.protocols.basic import LineOnlyReceiver import threading class ChatProtocol(LineOnlyReceiver): name = "" def getName(self): if self.name!="": return self.name return self.transport.getPeer().host def connectionMade(self): print "New connection from "+self.getName() self.sendLine("Welcome to the chat.\r\n/quit to quit.") self.factory.sendMessageToAllClients(self.getName()+" joined the chat.") self.factory.clientProtocols.append(self) def connectionLost(self, reason): print "Lost connection from "+self.getName() self.factory.clientProtocols.remove(self) self.factory.sendMessageToAllClients(self.getName()+" disconnected.") def lineReceived(self, line): if line=="/quit": self.transport.loseConnection() else: self.factory.sendMessageToAllClients(self.getName()+": "+line) def sendLine(self, line): self.transport.write(line+"\r\n") class ChatProtocolFactory(ServerFactory): protocol = ChatProtocol def __init__(self): self.clientProtocols = [] def sendMessageToAllClients(self, mesg): for client in self.clientProtocols: client.sendLine(mesg) def Test(): global test factory.sendMessageToAllClients('Hey.') test = threading.Timer(1.0, Test) test.start() test = threading.Timer(1.0, Test) test.start() print "Starting Server" factory = ChatProtocolFactory() reactor.listenTCP(3009, factory) reactor.run() ---------- The problem is that last def, "def Test()". It should send the line "Hey." to all connected users every second. It does that - however, the message only sends through if the client is typing or has entered a line. So, if a client is idle for 30 seconds, then starts typing, he will receive "Hey." 30 times in a row - one for each second he was idle. Is there any way to get this to send the message and have it show up for a client even when they're idle?

This message is more appropriate for the twisted-python list, as it has nothing to do with "web", so I've cross-posted it there. On 3 Jun, 10:57 pm, woahyeahyeah@yahoo.com wrote:
I've got a simple little chat server set up that people can telnet into.� The code is as follows:
from twisted.internet import reactor from twisted.internet.protocol import ServerFactory from twisted.protocols.basic import LineOnlyReceiver import threading
^ This is where things start to go (horribly) wrong. The protocol looks basically correct, but this part:
def Test(): ��� global test ��� factory.sendMessageToAllClients('Hey.') ��� test = threading.Timer(1.0, Test) ��� test.start() test = threading.Timer(1.0, Test) test.start()
is very, very wrong. You cannot call Twisted APIs from a thread. Ever. This is very important :). The behavior to you appeared to be some strange timing issues, but you can get anything from corrupted data to crashing the entire program.
Is there any way to get this to send the message and have it show up for a client even when they're idle?
Yes. Use the reactor's timed-event APIs rather than threads. In other words, do this, after you create your factory: from twisted.internet.task import LoopingCall LoopingCall(factory.sendMessageToAllClients, 'Hey.').start(1.0) You may be interested to also read about these APIs: http://twistedmatrix.com/documents/8.2.0/api/twisted.internet.task.LoopingCa... http://twistedmatrix.com/documents/8.2.0/api/twisted.internet.interfaces.IRe...

2009/6/3 Jason St.Clair <woahyeahyeah@yahoo.com>:
I've got a simple little chat server set up that people can telnet into. The code is as follows:
----------
from twisted.internet import reactor from twisted.internet.protocol import ServerFactory from twisted.protocols.basic import LineOnlyReceiver import threading
class ChatProtocol(LineOnlyReceiver):
name = ""
def getName(self): if self.name!="": return self.name return self.transport.getPeer().host
def connectionMade(self): print "New connection from "+self.getName() self.sendLine("Welcome to the chat.\r\n/quit to quit.") self.factory.sendMessageToAllClients(self.getName()+" joined the chat.") self.factory.clientProtocols.append(self)
def connectionLost(self, reason): print "Lost connection from "+self.getName() self.factory.clientProtocols.remove(self) self.factory.sendMessageToAllClients(self.getName()+" disconnected.")
def lineReceived(self, line): if line=="/quit": self.transport.loseConnection() else: self.factory.sendMessageToAllClients(self.getName()+": "+line)
def sendLine(self, line): self.transport.write(line+"\r\n")
class ChatProtocolFactory(ServerFactory):
protocol = ChatProtocol
def __init__(self): self.clientProtocols = []
def sendMessageToAllClients(self, mesg): for client in self.clientProtocols: client.sendLine(mesg)
def Test(): global test factory.sendMessageToAllClients('Hey.') test = threading.Timer(1.0, Test) test.start() test = threading.Timer(1.0, Test) test.start()
print "Starting Server" factory = ChatProtocolFactory() reactor.listenTCP(3009, factory) reactor.run()
----------
The problem is that last def, "def Test()". It should send the line "Hey." to all connected users every second. It does that - however, the message only sends through if the client is typing or has entered a line. So, if a client is idle for 30 seconds, then starts typing, he will receive "Hey." 30 times in a row - one for each second he was idle.
Is there any way to get this to send the message and have it show up for a client even when they're idle? _______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
You should use twisted.internet.task.LoopingCall instead of threading.Timer and it's also worth mentioning that twisted APIs are *not* thread-safe; in use cases where you have to use threads (there are really few), you should use reactor.callFromThread(). So the equivalent for the above using LoopingCall would be something like: def test(): factory.sendMessageToAllClients('Hey.') task = LoopingCall(test) reactor.callWhenRunning(task.start, 1.0, 0) ... HTH -Drew

2009/6/4 Drew Smathers <drew.smathers@gmail.com>:
2009/6/3 Jason St.Clair <woahyeahyeah@yahoo.com>:
I've got a simple little chat server set up that people can telnet into. The code is as follows:
----------
from twisted.internet import reactor from twisted.internet.protocol import ServerFactory from twisted.protocols.basic import LineOnlyReceiver import threading
class ChatProtocol(LineOnlyReceiver):
name = ""
def getName(self): if self.name!="": return self.name return self.transport.getPeer().host
def connectionMade(self): print "New connection from "+self.getName() self.sendLine("Welcome to the chat.\r\n/quit to quit.") self.factory.sendMessageToAllClients(self.getName()+" joined the chat.") self.factory.clientProtocols.append(self)
def connectionLost(self, reason): print "Lost connection from "+self.getName() self.factory.clientProtocols.remove(self) self.factory.sendMessageToAllClients(self.getName()+" disconnected.")
def lineReceived(self, line): if line=="/quit": self.transport.loseConnection() else: self.factory.sendMessageToAllClients(self.getName()+": "+line)
def sendLine(self, line): self.transport.write(line+"\r\n")
class ChatProtocolFactory(ServerFactory):
protocol = ChatProtocol
def __init__(self): self.clientProtocols = []
def sendMessageToAllClients(self, mesg): for client in self.clientProtocols: client.sendLine(mesg)
def Test(): global test factory.sendMessageToAllClients('Hey.') test = threading.Timer(1.0, Test) test.start() test = threading.Timer(1.0, Test) test.start()
print "Starting Server" factory = ChatProtocolFactory() reactor.listenTCP(3009, factory) reactor.run()
----------
The problem is that last def, "def Test()". It should send the line "Hey." to all connected users every second. It does that - however, the message only sends through if the client is typing or has entered a line. So, if a client is idle for 30 seconds, then starts typing, he will receive "Hey." 30 times in a row - one for each second he was idle.
Is there any way to get this to send the message and have it show up for a client even when they're idle? _______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
You should use twisted.internet.task.LoopingCall instead of threading.Timer and it's also worth mentioning that twisted APIs are *not* thread-safe; in use cases where you have to use threads (there are really few), you should use reactor.callFromThread().
So the equivalent for the above using LoopingCall would be something like:
def test(): factory.sendMessageToAllClients('Hey.') task = LoopingCall(test) reactor.callWhenRunning(task.start, 1.0, 0) ...
HTH
-Drew
Also, forgot to mention: This is a general question better asked at twisted-python mailing list (not twisted-web). -Drew
participants (3)
-
Drew Smathers
-
glyph@divmod.com
-
Jason St.Clair