I'm working with this example of an iPhone Chart Server and all is working as expected.
What I wanted to ask is if and how I can use message(self, message) outside the IphoneChat class...
For example if I have an event triggering every hour I want to be able to send everyone connected a message or if I want to take the server down to send a 'global' announcement, do I have to put all the code within the IphoneChat class?
The server.py is this:
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor
class IphoneChat(Protocol):
def connectionMade(self):
self.factory.clients.append(self)
print "clients are ", self.factory.clients
def connectionLost(self, reason):
self.factory.clients.remove(self)
# define message handling...
def dataReceived(self, data):
a = data.split(':')
print a
if len(a) > 1:
command = a[0]
content = a[1]
msg = ""
if command == "iam":
#msg = content + " has joined"
msg = "test1"
elif command == "toggle":
#msg = command + ": " + content
msg = "test2"
elif command == "msg":
msg = command + ": " + content
print msg
for c in self.factory.clients:
c.message(msg)
def message(self, message):
self.transport.write(message + '\n')
rt = pollTimer.RepeatedTimer(3, NotifyAllFunction)
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
reactor.listenTCP(6035, factory)
print "chat server started"
reactor.run()
Adding the polling module:
from threading import Timer
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
Let's say you registered a callback to be executed after some time, then you can simple access all the clients from factory.clients and send them a message using their .transport.write() method:
from twisted.internet import task
...
# Rest of the code
...
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
def broadcast(message):
for client in factory.clients:
client.transport.write(message + '\n')
event = task.LoopingCall(broadcast, 'Ping to all users')
event.start(60*60) # call every hour
reactor.listenTCP(6035, factory)
print "chat server started"
reactor.run()
Related
This test doesn't work.
class PrintHandler(MessageHandler):
def handle_message(self, message):
print(message)
class FileHandler(MessageHandler):
def handle_message(self, message):
with open('nana', 'w') as f:
f.write(message)
class SubscribeProcess(Process):
def __init__(self, handler):
super(SubscribeProcess, self).__init__(group=None, target=None, name=None, args=(), kwargs={})
self.handler = handler
def run(self):
self.address = TcpAddress(host='127.0.0.1', port=5555)
subscriber = ZmqSubscriber(ZmqBlockingConnection(address=self.address, bind=False))
subscriber.set_message_handler(self.handler)
print('............')
class TestZmqSubscriber(TestCase):
def test_set_message_handler(self):
address = TcpAddress(host='127.0.0.1', port=5555)
pub_connection = ZmqBlockingConnection(address, bind=True)
publisher = ZmqPublisher(pub_connection)
p = SubscribeProcess(handler=PrintHandler())
p.start()
while True:
publisher.publish('Message number {}'.format(2))
I now that's this is not the unit test actually. But I want to see the received messages in console first. Then I will write proper test.
While this two scripts work perfectly.
connection = ZmqBlockingConnection(TcpAddress(host='127.0.0.1', port=5555), bind=False)
sub = ZmqSubscriber(connection)
sub.set_message_handler(PrintHandler())
address = TcpAddress(host='127.0.0.1', port=5555)
pub_connection = ZmqBlockingConnection(address, bind=True)
publisher = ZmqPublisher(pub_connection)
while True:
publisher.publish('Message number {}'.format(2))
Inside of subscriber.set_message_handler(handler) is actually this
def start_receiving_messages(self, message_handler):
while True:
message_handler.handle_message(self.socket.recv())
And in debugger I see that the code hangs infinitely in socket.recv()
Maybe I'm using multiprocessing wrong?
EDIT1
class ZmqBlockingConnection(Connection):
def start_receiving_messages(self, message_handler):
while True:
message_handler.handle_message(self.socket.recv())
def send_message(self, message):
self.socket.send(message)
def __init__(self, address, bind, hwm=1000):
self.hwm = hwm
self.bind = bind
self.address = address
self.socket = None
def set_hwm(self, hwm):
self.socket.set_hwm(hwm)
def configure(self, socket_type):
self.socket = zmq.Context().socket(socket_type)
if self.bind:
self.socket.bind(str(self.address))
else:
self.socket.connect(str(self.address))
self.set_hwm(self.hwm)
OK, the problem was in
def configure(self, socket_type):
self.socket = zmq.Context().instance().socket(socket_type)
if self.bind:
self.socket.bind(str(self.address))
else:
self.socket.connect(str(self.address))
self.set_hwm(self.hwm)
so instead of using singleton I started to create context instances and now it's working.
I have problem figuring out how to use zmq with ioloops and multithreading. Whatever I'm doing I have some exceptions.
__author__ = 'michael'
class ZmqLoopRunner(Thread):
def __init__(self, callback):
super(ZmqLoopRunner, self).__init__()
self.loop = IOLoop.current()
self.callback = callback
def run(self):
self.loop.start()
print('loop have been stopped')
self.callback()
def stop(self):
self.loop.stop()
class BaseZmqNode():
__metaclass__ = ABCMeta
def __init__(self, host, port, bind, hwm):
self.node = self.create_node()
self.node.host = host
self.port = port
self.context = zmq.Context().instance()
self.socket = self.create_socket()
if bind:
self.socket.bind(self.build_address(host, port))
else:
self.socket.connect(self.build_address(host, port))
self.set_hwm(hwm)
def set_hwm(self, hwm):
self.socket.set_hwm(hwm)
def send_multipart(self, message):
self.socket.send_multipart(message)
def send_json(self, json):
self.socket.send_json(json)
def create_node(self):
return BaseMessagingNode
def close(self):
self.socket.close()
#staticmethod
def build_address(host, port):
strings = [host, ':', str(port)]
return ''.join(strings)
#abstractmethod
def create_socket(self):
pass
class BaseZmqReceiver(BaseZmqNode):
__metaclass__ = ABCMeta
def __init__(self, host, port, hwm, bind, on_receive_callback):
super(BaseZmqReceiver, self).__init__(host=host, port=port, bind=bind, hwm=hwm)
self.node.on_message_callback = on_receive_callback
self.stream = ZMQStream(self.socket)
self.stream.on_recv(self.on_message_received)
self.runner = ZmqLoopRunner(self.stream.close)
self.runner.start()
def on_message_received(self, message):
return self.node.on_message_callback(message)
def create_node(self):
return ReceivingNode(None, None)
def close(self):
# super(BaseZmqReceiver, self).close()
self.runner.stop()
# self.socket.close()
Here is how my code looks right now. I have exceptions in test 'Address already in use' when running my tests.
Here is stacktrace
Traceback (most recent call last):
File "/opt/leos/code/messaging_system/tests/ZmqTest.py", line 51, in test_send_json
publisher = ZmqPublisher('tcp://*', 6000)
File "/opt/leos/code/messaging_system/zeromq/ZmqPublisher.py", line 11, in __init__
super(ZmqPublisher, self).__init__(host=host, port=port, bind=bind, hwm=hwm)
File "/opt/leos/code/messaging_system/zeromq/BaseZmqNode.py", line 18, in __init__
self.socket.bind(self.build_address(host, port))
File "socket.pyx", line 434, in zmq.backend.cython.socket.Socket.bind (zmq/backend/cython/socket.c:3928)
File "checkrc.pxd", line 21, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:6058)
ZMQError: Address already in use
class ZmqTest(AbstractMessagingTest):
def setUp(self):
super(ZmqTest, self).setUp()
self.multipart_messages = self.create_multipart_messages(10)
def tearDown(self):
super(ZmqTest, self).tearDown()
def test_request_reply(self):
requester = ZmqReq(host='tcp://localhost', port=6000)
self.request = 'Hello'
self.reply = 'World!'
replier = ZmqRep(host='tcp://*', port=6000, request_processor=self.on_request_received)
self.assertEqual(self.reply, requester.execute(request=self.request))
# requester.close()
replier.close()
requester.close()
def test_address_creation(self):
full_address = "tcp://localhost:5559"
self.assertEqual(full_address, ZmqSubscriber.build_address("tcp://localhost", 5559))
self.assertEqual('tcp://*:6000', ZmqPublisher.build_address("tcp://*", 6000))
def test_publisher_subscriber(self):
publisher = ZmqPublisher('tcp://*', 6000)
subscriber = ZmqSubscriber('tcp://localhost', 6000, self.handle_message)
self.send_messages(publisher, wait=False)
sleep(0.5)
self.assertSequenceEqual(self.test_messages, self.received_messages)
publisher.close()
subscriber.close()
def handle_message(self, message):
self.base_handle_message(message[0])
def test_send_json(self):
publisher = ZmqPublisher('tcp://*', 6000)
subscriber = ZmqSubscriber('tcp://localhost', 6000, self.handle_json_message)
md = {'1' : 1}
publisher.send_json(md)
publisher.close()
subscriber.close()
def create_multipart_messages(self, size):
messages = []
for i in range(size):
messages.append(['Multipart test message', str(i)])
return messages
def send_multipart_messages(self, sender):
for message in self.multipart_messages:
sender.send_multipart(message)
def test_multipart_messages(self):
publisher = ZmqPublisher('tcp://*', 6000)
subscriber = ZmqSubscriber('tcp://localhost', 6000, self.base_handle_message)
self.send_multipart_messages(publisher)
sleep(0.5)
self.assertSequenceEqual(self.multipart_messages, self.received_messages)
publisher.close()
subscriber.close()
def test_push_pull_multipart(self):
ventilator = ZmqPush('tcp://*', 6000)
worker = ZmqPull('tcp://localhost', 6000, self.base_handle_message)
self.send_multipart_messages(ventilator)
sleep(0.5)
self.assertSequenceEqual(self.multipart_messages, self.received_messages)
ventilator.close()
worker.close()
def handle_json_message(self, json):
print(str(json))
def test_push_pull(self):
ventilator = ZmqPush('tcp://*', 6000)
worker = ZmqPull('tcp://localhost', 6000, self.handle_message)
self.send_messages(ventilator, wait=False)
sleep(0.5)
self.assertSequenceEqual(self.test_messages, self.received_messages)
ventilator.close()
worker.close()
def on_request_received(self, message):
if message[0] == self.request:
return self.reply
else:
return 'ERROR'
And I've tried many variants of this code. Like as you can see right now I'm trying to close the stream after the loop.start() method returned. I've tried to close the stream after the stop method and it just don't work.
So you're getting the error that the socket address is already open. This is probably because you have run up the program, an exception was thrown and you haven't closed down the socket.
I'd suggest some try, except and finally blocks:
try:
requester = ZmqReq(host='tcp://localhost', port=6000)
self.request = 'Hello'
self.reply = 'World!'
replier = ZmqRep(host='tcp://*', port=6000, request_processor=self.on_request_received)
self.assertEqual(self.reply, requester.execute(request=self.request))
except Exception as e:
# You can catch exceptions here
pass
finally:
# Once the code completes or exceptions are thrown, clean up
replier.close()
requester.close()
Can you anyone please help me (noob) call the broadcast function from class BroadcastServerFactory in class process, as per attached code
I have tried so many methods of call a function from another class, but no solution
import time, sys
from apscheduler.scheduler import Scheduler
import threading
import socket
from twisted.internet import reactor
from twisted.python import log
from twisted.web.server import Site
from twisted.web.static import File
from autobahn.websocket import WebSocketServerFactory, \
WebSocketServerProtocol, \
listenWS
class process(threading.Thread):
def __init__(self, buffer3):
threading.Thread.__init__(self)
self.setDaemon(True)
self.buffer3 = buffer3
def run(self):
factory.broadcast("I don't know what I'm doing!")
class BroadcastServerProtocol(WebSocketServerProtocol):
def onOpen(self):
self.factory.register(self)
def onMessage(self, msg, binary):
if not binary:
self.factory.broadcast("'%s' from %s" % (msg, self.peerstr))
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
self.factory.unregister(self)
class BroadcastServerFactory(WebSocketServerFactory):
"""
Simple broadcast server broadcasting any message it receives to all
currently connected clients.
"""
def __init__(self, url, debug = False, debugCodePaths = False):
WebSocketServerFactory.__init__(self, url, debug = debug, debugCodePaths = debugCodePaths)
self.clients = []
self.tickcount = 0
self.tick()
def tick(self):
self.tickcount += 1
self.broadcast("'tick %d' from server" % self.tickcount)
reactor.callLater(1, self.tick)
def register(self, client):
if not client in self.clients:
print "registered client " + client.peerstr
self.clients.append(client)
def unregister(self, client):
if client in self.clients:
print "unregistered client " + client.peerstr
self.clients.remove(client)
def broadcast(self, msg):
print "broadcasting message '%s' .." % msg
for c in self.clients:
c.sendMessage(msg)
print "message sent to " + c.peerstr
class BroadcastPreparedServerFactory(BroadcastServerFactory):
"""
Functionally same as above, but optimized broadcast using
prepareMessage and sendPreparedMessage.
"""
def broadcast(self, msg):
print "broadcasting prepared message '%s' .." % msg
preparedMsg = self.prepareMessage(msg)
for c in self.clients:
c.sendPreparedMessage(preparedMsg)
print "prepared message sent to " + c.peerstr
def testing():
buffer2 - "hello"
myDisplay = process(buffer2)
myDisplay.start()
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
log.startLogging(sys.stdout)
debug = True
else:
debug = False
level_scheduler = Scheduler()
level_scheduler.add_interval_job(testing, seconds=5)
level_scheduler.start()
#ServerFactory = BroadcastServerFactory
ServerFactory = BroadcastPreparedServerFactory
factory = ServerFactory("ws://localhost:9000",
debug = debug,
debugCodePaths = debug)
factory.protocol = BroadcastServerProtocol
factory.setProtocolOptions(allowHixie76 = True)
listenWS(factory)
webdir = File(".")
web = Site(webdir)
reactor.listenTCP(8080, web)
reactor.run()
Thanks
Pass the class instance of BroadcastServerFactory to be called to the class instance that calls it process on creation
class process(threading.Thread):
def __init__(self, buffer3m, broadcast_server_factory):
threading.Thread.__init__(self)
self.setDaemon(True)
self.buffer3 = buffer3
self.factory = broadcast_server_factory
def run(self):
self.factory.broadcast("I don't know what I'm doing!")
and then call it (it's assigned as self.factory in the run statement. I can't see where you create a process class in your __main__ but it will be created with something like
p = process(buffer, factory)
Aside: Using capital letters for class names is considered good form in python process -> Process
I've been playing around with the Twisted extension and have fooled around with a chat room sort of system. However I want to expand upon it. As of now it only support multi-client chats with usernames ect. But I want to try and use the pyAudio extension to build a sort of VoIP application. I have a clientFactory and a simple echo protocol in the client and a serverFactory and protocol on the server. but I'm not entirely sure how to build off of this. What would be the best way to go about doing something like this?
Code to help if you need it
client.py:
import wx
from twisted.internet import wxreactor
wxreactor.install()
# import twisted reactor
import sys
from twisted.internet import reactor, protocol, task
from twisted.protocols import basic
class ChatFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title="WhiteNOISE")
self.protocol = None # twisted Protocol
sizer = wx.BoxSizer(wx.VERTICAL)
self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY)
self.ctrl = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER, size=(300, 25))
sizer.Add(self.text, 5, wx.EXPAND)
sizer.Add(self.ctrl, 0, wx.EXPAND)
self.SetSizer(sizer)
self.ctrl.Bind(wx.EVT_TEXT_ENTER, self.send)
def send(self, evt):
self.protocol.sendLine(str(self.ctrl.GetValue()))
self.ctrl.SetValue("")
class DataForwardingProtocol(basic.LineReceiver):
def __init__(self):
self.output = None
def dataReceived(self, data):
gui = self.factory.gui
gui.protocol = self
if gui:
val = gui.text.GetValue()
gui.text.SetValue(val + data)
gui.text.SetInsertionPointEnd()
def connectionMade(self):
self.output = self.factory.gui.text # redirect Twisted's output
class ChatFactory(protocol.ClientFactory):
def __init__(self, gui):
self.gui = gui
self.protocol = DataForwardingProtocol
def clientConnectionLost(self, transport, reason):
reactor.stop()
def clientConnectionFailed(self, transport, reason):
reactor.stop()
if __name__ == '__main__':
app = wx.App(False)
frame = ChatFrame()
frame.Show()
reactor.registerWxApp(app)
reactor.connectTCP("192.168.1.115", 5001, ChatFactory(frame))
reactor.run()
server.py:
from twisted.internet import reactor, protocol
from twisted.protocols import basic
import time
def t():
return "["+ time.strftime("%H:%M:%S") +"] "
class EchoProtocol(basic.LineReceiver):
name = "Unnamed"
def connectionMade(self):
#on client connection made
self.sendLine("WhiteNOISE")
self.sendLine("Enter A Username Below...")
self.sendLine("")
self.count = 0
self.factory.clients.append(self)
print t() + "+ Connection from: "+ self.transport.getPeer().host
def connectionLost(self, reason):
#on client connection lost
self.sendMsg("- %s left." % self.name)
print t() + "- Connection lost: "+ self.name
self.factory.clients.remove(self)
def lineReceived(self, line):
#actions to do on message recive
if line == '/quit':
#close client connection
self.sendLine("Goodbye.")
self.transport.loseConnection()
return
elif line == "/userlist":
#send user list to single client, the one who requested it
self.chatters()
return
elif line.startswith("/me"):
#send an action formatted message
self.sendLine("**" + self.name + ": " + line.replace("/me",""))
return
elif line == "/?":
self.sendLine("Commands: /? /me /userlist /quit")
return
if not self.count:
self.username(line)
else:
self.sendMsg(self.name +": " + line)
def username(self, line):
#check if username already in use
for x in self.factory.clients:
if x.name == line:
self.sendLine("This username is taken; please choose another")
return
self.name = line
self.chatters()
self.sendLine("You have been connected!")
self.sendLine("")
self.count += 1
self.sendMsg("+ %s joined." % self.name)
print '%s~ %s connected as: %s' % (t(), self.transport.getPeer().host, self.name)
def chatters(self):
x = len(self.factory.clients) - 1
s = 'is' if x == 1 else 'are'
p = 'person' if x == 1 else 'people'
self.sendLine("There %s %i other %s connected:" % (s, x, p) )
for client in self.factory.clients:
if client is not self:
self.sendLine(client.name)
self.sendLine("")
def sendMsg(self, message):
#send message to all clients
for client in self.factory.clients:
client.sendLine(t() + message)
class EchoServerFactory(protocol.ServerFactory):
protocol = EchoProtocol
clients = []
if __name__ == "__main__":
reactor.listenTCP(5001, EchoServerFactory())
reactor.run()
Take a look at Divmod Sine, a SIP application server. The basic idea is that you need an additional network server in your application that will support the VoIP parts of the application.
My goal here is to make a simple server handling TCP message like "process My String" that send "My String" to be processed by a quite time taking operation called (here slowFunction). Here I'm calling this function through deferToThread but nothing seems to happened : the defered's callback messages doesn't show up anywhere (breakpoints show it's never called) and the print in the function aren't displayed (breakpoints show it's never called)
from twisted import protocols
from twisted.protocols import basic
from twisted.internet import threads, protocol, reactor
from twisted.application import service, internet
import re
import time
def slowFunction(arg):
print "starting process"
time.sleep(1)
print "processed "+arg
class MySimpleServer(basic.LineReceiver):
PROCESS_COMMAND = "process (.*)" #re pattern
processFunction = slowFunction
clients = []
def connectionMade(self):
print "Client connected"
MySimpleServer.clients.append(self)
def connectionLost(self, reason):
print "Client gone"
MySimpleServer.clients.remove(self)
def onProcessDone(self):
self.message("Process done")
def onError(self):
self.message("Process fail with error")
def lineReceived(self, line):
processArgumentResult = re.search(MySimpleServer.PROCESS_COMMAND, line)
if not processArgumentResult == None:
processArgument = processArgumentResult.groups()[0]
deferred = threads.deferToThread(MySimpleServer.processFunction, processArgument)
deferred.addCallback(self.onProcessDone)
deferred.addErrback(self.onError)
self.message("processing your request")
else:
print "Unknown message line: "+line
def message(self, message):
self.transport.write(message + '\n')
if __name__ == '__main__':
factory = protocol.ServerFactory()
factory.protocol = MySimpleServer
factory.client = []
reactor.listenTCP(8000, factory)
reactor.run()
I've got some help by the guys of twisted irc
The points are : the callback (onProcessDone and onError) are supposed to take a result argument, and the function called by deferToThread will receive self as an argument (it should one of the MySimpleServer class' method).
The final code is now :
from twisted import protocols
from twisted.protocols import basic
from twisted.internet import threads, protocol, reactor
from twisted.application import service, internet
import re
import time
def slowFunction(arg):
print "starting process"
time.sleep(20)
print "processed "+arg
class MySimpleServer(basic.LineReceiver):
PROCESS_COMMAND = "process (.*)" #re pattern
clients = []
def connectionMade(self):
print "Client connected"
MySimpleServer.clients.append(self)
def connectionLost(self, reason):
print "Client gone"
MySimpleServer.clients.remove(self)
def onProcessDone(self, result):
self.message("Process done")
def onError(self, result):
self.message("Process fail with error")
def processFunction(self, processArgument):
slowFunction(processArgument)
def lineReceived(self, line):
processArgumentResult = re.search(MySimpleServer.PROCESS_COMMAND, line)
if not processArgumentResult == None:
processArgument = processArgumentResult.groups()[0]
deferred = threads.deferToThread(self.processFunction, processArgument)
deferred.addCallback(self.onProcessDone)
deferred.addErrback(self.onError)
self.message("processing your request")
else:
print "Unknown message line: "+line
def message(self, message):
self.transport.write(message + '\n')
if __name__ == '__main__':
factory = protocol.ServerFactory()
factory.protocol = MySimpleServer
factory.client = []
reactor.listenTCP(8000, factory)
reactor.run()
Another way you could've done this is with staticmethod; this is the only legitimate use for it.
class MySimpleServer(basic.LineReceiver):
processFunction = staticmethod(slowFunction)