How to use gevent with Kombu - rabbitmq

I am writing a basic publish-subscribe script in python using kombu and rabbitmq along with gevent. How can we switch context to the publisher when the consumer is waiting on the queue until a message arrives? I tried using gevent.sleep() but its not working in my case
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue May 30 12:15:56 2017
#author: kartikkillawala
"""
from kombu import Connection,Exchange,Queue,Producer
from kombu.mixins import ConsumerMixin
import gevent
from gevent import Timeout
class Worker(ConsumerMixin):
def __init__(self,connection,queues):
self.connection=connection
self.queues=queues
def get_consumers(self,Consumer,channel):
return([Consumer(queues=self.queues,callbacks= self.on_message] )])
def on_message(self,body,message):
print("Got Message: {0}".format(body))
message.ack()
self.should_stop=True
gevent.sleep(1)
class MessageVerify:
exchange_name = "Test-Exchange"
exchange_type = "direct"
queue_name = "Test-Queue"
routing_key = "ABC"
connection_producer = None
channel_producer = None
exchange_producer = None
producer = None
consumer = None
queue = None
worker = None
def __init__(self,rabbitmq1):
self.connection_producer = Connection(hostname=rabbitmq1['host'],userid=rabbitmq1['username'],password=rabbitmq1['password'])
self.channel_producer = self.connection_producer.channel()
self.exchange_producer = Exchange(name =self.exchange_name,type=self.exchange_type)
self.queue = Queue(name=self.queue_name,exchange=self.exchange_producer,routing_key=self.routing_key)
self.queue.maybe_bind(self.connection_producer)
self.queue.declare()
def sendMessage(self,routing_key,data):
self.producer = Producer(channel=self.channel_producer,exchange=self.exchange_producer,routing_key=routing_key)
self.producer.publish(data,routing_key=routing_key)
print("[p]Published Message",data,'with routing key',routing_key)
gevent.sleep(2)
def receiveMessage(self,routing_key,timeout=120):
try:
print ("Worker run")
timer = Timeout(timeout)
timer.start()
gevent.sleep(2)
self.worker = Worker(self.connection_producer,self.queue)
self.worker.run()
except Timeout:
print("Connection Timeout")
rabbitmq1 = {
"username":"guest",
"password":"guest",
"host":"localhost"}
msg_verify = MessageVerify(rabbitmq1)
threads = [gevent.spawn(msg_verify.receiveMessage("ABC",timeout=120)),gevent.spawn(msg_verify.sendMessage("ABC","Sample Message"))]
gevent.joinall(threads)
print ("After joinall")

Related

PyQt5 QTimer in not working after moving it to a thread

I am currently trying to separate my PyQT5-GUI from my serial communication to prevent a freezing GUI.
Therefore, I tried to implement threading. So when I am pressing a button "Open Port", a new thread starts which handles this incoming and outgoing data of the Port.
Now, this works fine, but I am having problems with a timer not fulfilling its function. Essentially, I want to close the port after a 'no valid' message has been received for x seconds.
I tried to create a minimum example:
Upon starting, the GUI and the thread are created. When pressing "Open", the port is opened and the timer in the thread should start. After 3000 milliseconds, the port should be closed by the timer overflow.
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import QtSerialPort
import sys
class AECRobotControl(QMainWindow):
signal_open_port = pyqtSignal(str)
signal_close_port = pyqtSignal(int)
def __init__(self):
super().__init__()
# main-window setup
self.ui = Ui_RobotControls_Main()
self.ui.setupUi(self)
# Port
self.port_gcode_timer_sending = QTimer() # Timer for sending Gcode to prevent robot queue overflow
# END Port
# Threads
self.thread_port = QThread()
self.thread_port_worker = None
# END Threads
# END Variables
# Functions
# Init the ui with standard values
# self.init_ui()
# Init COM-worker
self.init_worker()
# Init signal callbacks
self.init_signals()
# show GUI
self.show()
# END Functions
def init_ui(self):
self.layout.addWidget(self.button_open)
self.layout.addWidget(self.button_close)
self.setLayout(self.layout)
def init_signals(self):
# Button Open Port
self.ui.pushButton_open_port.clicked.connect(self.button_open_comport)
# END Button Open Port
# Button Close Port
self.ui.pushButton_close_port.clicked.connect(self.button_close_comport)
# END Button Close Port
def init_worker(self):
self.thread_port_worker = RobotMessageThread()
self.thread_port_worker.moveToThread(self.thread_port)
self.thread_port.started.connect(self.thread_port_worker.start_thread)
self.thread_port.finished.connect(self.thread_port.deleteLater)
self.thread_port_worker.finished.connect(self.thread_port.quit)
self.thread_port_worker.finished.connect(self.thread_port_worker.deleteLater)
self.signal_open_port.connect(self.thread_port_worker.open_port_slot)
self.signal_close_port.connect(self.thread_port_worker.close_comport)
self.thread_port.start()
def button_open_comport(self):
self.signal_open_port.emit("COM4")
def button_close_comport(self):
if (self.thread_port.isRunning() == True):
self.signal_close_port.emit(0)
def parse_com_message(self, message):
try:
print(message)
except Exception as e:
print(e)
class RobotMessageThread(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
com_message_parsed = pyqtSignal(QByteArray)
com_ascii_message_parsed = pyqtSignal(str)
def __init__(self):
super().__init__()
self.port_com_port = QtSerialPort.QSerialPort()
self.port_name = None
self.port_is_alive_timer = QTimer() # Interprets valid received messages as alive-sign from robot.
""" Functions to be called upon start of thread"""
def start_thread(self):
print("Thread started")
self.port_is_alive_timer.timeout.connect(lambda: self.close_comport(1))
""" Inits the port"""
def _init_port(self):
self.port_com_port.setPortName(self.port_name)
self.port_com_port.setBaudRate(QtSerialPort.QSerialPort.BaudRate.Baud115200)
self.port_com_port.setParity(QtSerialPort.QSerialPort.Parity.NoParity)
self.port_com_port.setDataBits(QtSerialPort.QSerialPort.DataBits.Data8)
self.port_com_port.setStopBits(QtSerialPort.QSerialPort.StopBits.OneStop)
#pyqtSlot(bytearray)
def message_slot(self, message: bytearray):
self._write_to_port(message)
#pyqtSlot(str)
def open_port_slot(self, com_name: str):
self.port_name = com_name
self._init_port()
self._open_comport()
#pyqtSlot()
def close_port_slot(self, message: bytearray):
self.close_comport(0)
""" Tries to open the selected comport"""
def _open_comport(self):
# Check whether port is already open
if self.port_com_port.open(QIODevice.ReadWrite) == True:
self.port_com_port.setDataTerminalReady(True)
print("COM Opened")
# Reset message-buffer
self.port_is_alive_timer.start(3000)
else:
print("opening failed")
""" Closes the selected comport"""
def close_comport(self, source):
if self.port_com_port.isOpen() == True:
# Close port and delete queue
self.port_com_port.clear()
self.port_com_port.close()
# Stop timers
self.port_is_alive_timer.stop()
print("Closed by " + str(source))
else:
print("Closing failed")
# GUI
class Ui_RobotControls_Main(object):
def setupUi(self, RobotControls_Main):
RobotControls_Main.setObjectName("RobotControls_Main")
RobotControls_Main.resize(1024, 900)
RobotControls_Main.setMinimumSize(QSize(1024, 900))
RobotControls_Main.setMaximumSize(QSize(1600, 1200))
self.centralwidget = QtWidgets.QWidget(RobotControls_Main)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_12 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_12.setObjectName("gridLayout_12")
self.QGroupBox_port_settings = QtWidgets.QGroupBox(self.centralwidget)
self.QGroupBox_port_settings.setObjectName("QGroupBox_port_settings")
self.gridLayout_15 = QtWidgets.QGridLayout(self.QGroupBox_port_settings)
self.gridLayout_15.setObjectName("gridLayout_15")
self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
self.horizontalLayout_21.setObjectName("horizontalLayout_21")
self.pushButton_open_port = QtWidgets.QPushButton(self.QGroupBox_port_settings)
self.pushButton_open_port.setMaximumSize(QSize(100, 50))
self.pushButton_open_port.setObjectName("pushButton_open_port")
self.horizontalLayout_21.addWidget(self.pushButton_open_port)
self.gridLayout_15.addLayout(self.horizontalLayout_21, 0, 0, 1, 1)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.pushButton_close_port = QtWidgets.QPushButton(self.QGroupBox_port_settings)
self.pushButton_close_port.setObjectName("pushButton_close_port")
self.horizontalLayout_4.addWidget(self.pushButton_close_port)
self.gridLayout_15.addLayout(self.horizontalLayout_4, 0, 1, 1, 1)
self.gridLayout_12.addWidget(self.QGroupBox_port_settings, 0, 0, 1, 1)
RobotControls_Main.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(RobotControls_Main)
self.statusbar.setObjectName("statusbar")
RobotControls_Main.setStatusBar(self.statusbar)
self.retranslateUi(RobotControls_Main)
QMetaObject.connectSlotsByName(RobotControls_Main)
def retranslateUi(self, RobotControls_Main):
_translate = QCoreApplication.translate
RobotControls_Main.setWindowTitle(_translate("RobotControls_Main", "RobotControls"))
self.QGroupBox_port_settings.setTitle(_translate("RobotControls_Main", "Port settings"))
self.pushButton_open_port.setText(_translate("RobotControls_Main", "Open Port"))
self.pushButton_close_port.setText(_translate("RobotControls_Main", "Close Port"))
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = AECRobotControl()
app.exec() # QT main-threadtimer.start(TIME)
I found a solution. In the above code, just one line needs to be added: after moving the worker to the separate thread, the QTimer also has to be moved to the same thread. So, after self.thread_port_worker.moveToThread(self.thread_port), the line self.thread_port_worker.port_is_alive_timer.moveToThread(self.thread_port) needs to be added

How to write python unittest cases to mock redis connection (redis.StrictRedis) in Django

How can I mock the following function for connecting to Redis?
import redis
class RedisCache:
redis_instance = None
#classmethod
def set_connect(cls):
redis_instance = redis.StrictRedis(host='0.0.0.0', port=6379, password='xyz', charset='utf-8', decode_responses=True, socket_timeout=30)
return redis_instance
#classmethod
def get_conn(cls):
cls.redis_instance = cls.set_connect()
return cls.redis_instance
I looked for some solutions, but they were basically using fakeredis module. I wanted to have a simpler way to mock these functions.
Note-
data returned by the function: Redis<ConnectionPool<Connection<host=127.0.0.1,port=6379,db=0>>>
You can use patch() function to mock out redis.StrictRedis class. See where-to-patch
E.g.
redis_cache.py:
import redis
class RedisCache:
redis_instance = None
#classmethod
def set_connect(cls):
redis_instance = redis.StrictRedis(host='0.0.0.0', port=6379, password='xyz',
charset='utf-8', decode_responses=True, socket_timeout=30)
return redis_instance
#classmethod
def get_conn(cls):
cls.redis_instance = cls.set_connect()
return cls.redis_instance
test_redis_cache.py:
from unittest import TestCase
import unittest
from unittest.mock import patch, Mock
from redis_cache import RedisCache
class TestRedisCache(TestCase):
def test_set_connect(self):
with patch('redis.StrictRedis') as mock_StrictRedis:
mock_redis_instance = mock_StrictRedis.return_value
actual = RedisCache.set_connect()
self.assertEqual(actual, mock_redis_instance)
mock_StrictRedis.assert_called_once_with(host='0.0.0.0', port=6379, password='xyz',
charset='utf-8', decode_responses=True, socket_timeout=30)
#patch('redis.StrictRedis')
def test_get_conn(self, mock_StrictRedis):
mock_redis_instance = mock_StrictRedis.return_value
RedisCache.get_conn()
self.assertEqual(RedisCache.redis_instance, mock_redis_instance)
if __name__ == '__main__':
unittest.main()
test result:
..
----------------------------------------------------------------------
Ran 2 tests in 0.004s
OK
Name Stmts Miss Cover Missing
------------------------------------------------------------------------------
src/stackoverflow/70016401/redis_cache.py 11 0 100%
src/stackoverflow/70016401/test_redis_cache.py 18 0 100%
------------------------------------------------------------------------------
TOTAL 29 0 100%

Twisted will not send data back only if I use async DB ops

After struggling with inlineCallbacks and yield of twisted/txredisapi, I can save my data into redis. Thanks to author of txredisapi. Now I met a new issue, socket server will not send back to client before/after saving into DB.
Twisted offers simple socket server as following:
from twisted.internet import protocol, reactor
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data) ### write back
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
reactor.listenTCP(8000, EchoFactory)
recctor.run()
My code is similiar, only with additional DB ops.
#!/usr/bin/env python
import time
import binascii
import txredisapi
from twisted.internet import defer
from twisted.internet import protocol, reactor
from twisted.internet.protocol import Factory
from twisted.enterprise import adbapi
from twisted.python import log
from dmpack import Dmpack
from dmdb import Dmdb
from dmconfig import DmConf
dm = Dmpack()
conf = DmConf().loadConf()
rcs = txredisapi.lazyConnection(password=conf['RedisPassword'])
dbpool = adbapi.ConnectionPool("MySQLdb",db=conf['DbName'],user=conf['DbAccount'],\
passwd=conf['DbPassword'],host=conf['DbHost'],\
use_unicode=True,charset=conf['DbCharset'])
def getDataParsed(data):
realtime = None
period = None
self.snrCode = dm.snrToAscii(data[2:7])
realtime = data[7:167] # save it into redis
period = data[167:-2] # save it into SQL
return (snrCode, realtime, period)
class PlainTCP(protocol.Protocol):
def __init__(self, factory):
self.factory = factory
self.factory.numConnections = 0
self.snrCode = None
self.rData = None
self.pData = None
self.err = None
def connectionMade(self):
self.factory.numConnections += 1
print "Nr. of connections: %d\n" %(self.factory.numConnections)
self.transport.write("Hello remote\r\n") # it only prints very 5 connections.
def connectionLost(self, reason):
self.factory.numConnections -= 1
print "Nr. of connections: %d\n" %(self.factory.numConnections)
#defer.inlineCallbacks
def dataReceived(self, data):
global dbpool, rcs
(self.snrCode,rDat,pDat) = getDataParsed(data)
if self.snrCode == None or rDat == None or pDat == None:
err = "Bad format"
else:
err = "OK"
print "err:%s"%(err) # debug print to show flow control
self.err = err
self.transport.write(self.snrCode)
self.transport.write(self.err)
self.transport.write(rDat)
self.transport.write(pDat)
self.transport.loseConnection()
if self.snrCode != None and rDat != None and pDat != None:
res = yield self.saveRealTimeData(rcs, rDat)
res = yield self.savePeriodData(dbpool, pDat, conf)
print "err2:%s"%(err) # debug print to show flow control
#defer.inlineCallbacks
def saveRealTimeData(self, rc, dat):
key = "somekey"
val = "somedata"
yield rc.set(key,val)
yield rc.expire(key,30)
#defer.inlineCallbacks
def savePeriodData(self,rc,dat,conf):
query = "some SQL statement"
yield rc.runQuery(query)
class PlainTCPFactory(protocol.Factory):
def buildProtocol(self, addr):
return PlainTCP(self)
def main():
dmdb = Dmdb()
if not dmdb.detectDb():
print "Please run MySQL RDBS first."
sys.exit()
log.startLogging(sys.stdout)
reactor.listenTCP(8080, PlainTCPFactory())
reactor.run()
if __name__ == "__main__":
main()
And clip of my client, which is a simple client:
def connectSend(host="127.0.0.1",port=8080):
global packet
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((host, port))
s.sendall(''.join(packet))
data = s.recv(1024)
s.close()
print 'Received', repr(data)
except socket.error, err:
print "Remote socket is not available: %s"%str(err)
sys.exit(1)
The current status is:
If disable #defer.inlineCallbacks and yield opertions of dataReceived(), both self.transport.write() inside of connectionMode() and dataReceived() can output data to clients.
If we enabled #defer.inlineCallbacks and two yield DB ops of SQL/Redis, then self.transport.write() inside of connectionMode() prints every 5 connections, and dataReceived() will not output any data to clients.
the debug print statements will print on log regardless of #defer.inlineCallbacks anyway.
I was told that dataReceived() should not be #defer.inlineCallbacks. But it doesn't change anything if I removed that decoration.
I am thinking if gevent can help me out of this unpredicted behavior. I am twisted into an endless tornado, cyclone .....
Anyone who has similiar experience, please help me. Thanks.
By changing function as following, the code works.
#COMMENT OUT decorator of #defer.inlineCallbacks
def dataReceived(self, data):
global dbpool, rcs
(self.snrCode,rDat,pDat) = getDataParsed(data)
if self.snrCode == None or rDat == None or pDat == None:
err = "Bad format"
else:
err = "OK"
print "err:%s"%(err) # debug print to show flow control
self.err = err
self.transport.write(self.snrCode)
self.transport.write(self.err)
self.transport.write(rDat)
self.transport.write(pDat)
self.transport.loseConnection()
if self.snrCode != None and rDat != None and pDat != None:
self.saveRealTimeData(rcs, rDat)
self.savePeriodData(dbpool, pDat, conf)
# Removing yield before DB ops
print "err2:%s"%(err) # debug print to show flow control
#defer.inlineCallbacks
def saveRealTimeData(self, rc, dat):
print "saveRedis"
key = "somekey"
val = "somedata"
yield rc.set(key,val)
yield rc.expire(key,30)
#defer.inlineCallbacks
def savePeriodData(self,rc,dat,conf):
print "save SQL"
query = "some SQL statement"
yield rc.runQuery(query)
If we keep #defer.inlineCallbacks and yield in dataReceived. The connection is closed before second DB op. Therefore no data is output to connection. Maybe is caused by inlineCallbacks decorator.
By removing this, the flow control is simple and straightforward.
However, I still can get why I can not add inlineCallbacks if there are two deferred DB ops. This time they don't need deferred?

Twisted XMLRPC proxy

I'd like to make XMLRPC proxy with balancer using twisted.
[XMLRPC Server 1_1 8.8.8.8:8000] <--> [----------------------] <--- Client
[Proxy example.com:8000] <--- Client
[XMLRPC Server 1_2 9.9.9.9:8000] <--> [----------------------] <--- Client
So there are two XMLRPC instances which represents same methods. I need xmlrpc-proxy between this instances and clients. One more thing - this proxy should also accept JSON calls (kind of http://example.com:8000/RPC2, http://example.com:8000/JSON).
Right now I was trying to implement XMLRPC proxy calls. I can't receive answer back to client although sendLine() is calling.
import argparse
from twisted.internet import protocol, reactor, defer, threads
from twisted.web import xmlrpc
from twisted.internet.task import LoopingCall
from twisted.internet.defer import DeferredQueue, Deferred, inlineCallbacks
from twisted.protocols.basic import LineReceiver
import configfile
from bcsxmlrpc import xmlrpc_request_parser, xmlrpc_marshal
from customlogging import logging
logging.getLogger().setLevel(logging.DEBUG)
class ProxyClient(xmlrpc.Proxy):
def __init__(self, proxy_uri, user, timeout=30.0):
self.proxy_uri = proxy_uri
xmlrpc.Proxy.__init__(self, url=proxy_uri, user=user, connectTimeout=timeout)
#inlineCallbacks
def call_api(self, name, *args):
logging.debug(u"Calling API: %s" % name)
result = yield self.callRemote(name, *args)
proxy_pool.add(self.proxy_uri)
defer.returnValue(result)
class Request(object):
def __init__(self, method, params, deferred):
self.method = method
self.params = params
self.deferred = deferred
class ProxyServer(LineReceiver):
def dataReceived(self, data):
logging.pr(data)
params, method = xmlrpc_request_parser(data) # got method name and arguments
d = Deferred()
d.addCallbacks(self._send_reply, self._log_error)
logging.debug(u"%s%s added to queue" % (method, params))
queue.put(Request(method, params, d))
def _send_reply(self, result):
logging.ps(result)
self.sendLine(str(result))
def _log_error(self, error):
logging.error(error)
def connectionMade(self):
logging.info(u"New client connected")
def connectionLost(self, reason):
logging.info(u"Client connection lost: %s" % reason.getErrorMessage())
class ProxyServerFactory(protocol.Factory):
protocol = ProxyServer
def buildProtocol(self, addr):
return ProxyServer()
#inlineCallbacks
def _queue_execute_job():
if queue.pending and proxy_pool:
proxy = proxy_pool.pop()
request = yield queue.get()
result = yield ProxyClient(proxy, "").call_api(request.method, *list(request.params))
request.deferred.callback(result)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run configuration")
parser.add_argument('--config', help=u"Configuration file name/path")
config = configfile.ProxyConfig(parser.parse_args().config)
global proxy_pool
proxy_pool = set()
for proxy_server in config.servers:
proxy_pool.add(proxy_server)
global queue
queue = DeferredQueue()
lc2 = LoopingCall(_queue_execute_job)
lc2.start(1)
logging.info(u"Starting Proxy at port %s" % config.port)
reactor.listenTCP(config.port, ProxyServerFactory())
reactor.run()

strange error when receiving udp datagram

I have a file, mc_send.py, that send mcast messages and shall receive a unicastmessage back from the program that received the mcast message, mc_recv.py. mcast work but when receiving the unicast message back a strange error appear: ValueError: maximum length of data to be read cannot be negative The error is att line 14 in this file mc_send.py:
I have struggled with this many hours on windows7 with python2.7.2 and pyqt4 v4.9 but can't find what I'm doing wrong. This programs is based on the broadcast examples from pyqt4.
""" to see all ports on windows: netstat -ap udp | find "4545" """
from PyQt4 import QtCore, QtGui, QtNetwork
unicast_addr = "127.0.0.1"
unicast_port = 45455
mcast_addr = "239.255.43.21"
mcast_port = 45454
class Sender(QtGui.QDialog):
def processPendingDatagrams(self):
while self.udpServer.hasPendingDatagrams():
datagram, host, port = self.udpServer.readDatagram(self.udpSocket.pendingDatagramSize())
print "got msg:", datagram
def __init__(self, parent=None):
super(Sender, self).__init__(parent)
self.groupAddress = QtNetwork.QHostAddress(mcast_addr)
self.unicastAddress = QtNetwork.QHostAddress(unicast_addr)
self.statusLabel = QtGui.QLabel("Ready to multicast datagrams to group %s on port 45454" %
self.groupAddress.toString())
# setup socket for listening on incomming datagrams
self.udpServer = QtNetwork.QUdpSocket(self)
self.udpServer.bind(unicast_port)
self.udpServer.readyRead.connect(self.processPendingDatagrams)
self.startButton = QtGui.QPushButton("&Start")
self.quitButton = QtGui.QPushButton("&Quit")
buttonBox = QtGui.QDialogButtonBox()
buttonBox.addButton(self.startButton, QtGui.QDialogButtonBox.ActionRole)
buttonBox.addButton(self.quitButton, QtGui.QDialogButtonBox.RejectRole)
self.timer = QtCore.QTimer(self)
self.udpSocket = QtNetwork.QUdpSocket(self)
self.messageNo = 1
self.startButton.clicked.connect(self.startSending)
self.quitButton.clicked.connect(self.close)
self.timer.timeout.connect(self.send_mc_msg)
mainLayout = QtGui.QVBoxLayout()
mainLayout.addWidget(self.statusLabel)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
self.setWindowTitle("WSim")
def startSending(self):
self.startButton.setEnabled(False)
self.timer.start(1000)
def send_mc_msg(self):
self.udpSocket.writeDatagram("hello %d" %(self.messageNo), self.groupAddress, mcast_port)
self.messageNo += 1
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
sender = Sender()
sender.show()
sys.exit(sender.exec_())
The multicast receiver that also send the unicast response back looks like this mc_recv.py:
from PyQt4 import QtGui, QtNetwork
mcast_addr = "239.255.43.21"
mcast_port = 45454
answer_addr = "127.0.0.1"
answer_port = 45455
class Receiver(QtGui.QDialog):
def __init__(self, parent=None):
super(Receiver, self).__init__(parent)
self.groupAddress = QtNetwork.QHostAddress(mcast_addr)
self.udpSocket = QtNetwork.QUdpSocket(self)
self.udpSocket.bind(mcast_port, QtNetwork.QUdpSocket.ReuseAddressHint)
self.udpSocket.joinMulticastGroup(self.groupAddress)
self.udpSocket.readyRead.connect(self.processPendingDatagrams)
# Use this socket to send unicast messages to back
self.answerSocket = QtNetwork.QUdpSocket(self)
self.answerAddress = QtNetwork.QHostAddress(answer_addr)
quitButton = QtGui.QPushButton("&Quit")
quitButton.clicked.connect(self.close)
buttonLayout = QtGui.QHBoxLayout()
buttonLayout.addStretch(1)
buttonLayout.addWidget(quitButton)
buttonLayout.addStretch(1)
self.statusLabel = QtGui.QLabel("Listening for multicasted messages on %s" % mcast_addr)
mainLayout = QtGui.QVBoxLayout()
mainLayout.addWidget(self.statusLabel)
mainLayout.addLayout(buttonLayout)
self.setLayout(mainLayout)
self.setWindowTitle("mrecv")
def processPendingDatagrams(self):
"""receive and decode multicast messages and send a response message on the return address"""
while self.udpSocket.hasPendingDatagrams():
datagram, host, port = self.udpSocket.readDatagram(self.udpSocket.pendingDatagramSize())
self.statusLabel.setText("received mcast msg '%s'" % datagram)
# send a response back to msend
self.answerSocket.writeDatagram("hi back", self.answerAddress, answer_port)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
receiver = Receiver()
receiver.show()
sys.exit(receiver.exec_())
found the cause, embarrassingly simple error, had written self.udpSocket.pendingDatagramSize()
instead of self.udpServer.pendingDatagramSize() when I was reading the data...