I am currently working on a project that requires some communication over the network of a different data types from some entities of a distributed system and I am using ZMQ
Distributed systems need to operate both smart and efficiently, as the agents are distributed and both error-analyses and deployed production-issues are extremely expensive to analyse / test / debug.
Thus a copy/paste re-use of a problem-incompatible idea is not a way to achieve either the former, the less the latter.
client-[A].send()-s a message, that O/P wanted to become server-side-[S].recv()-ed and re-broadcast to all other clients-[B,C,...], except the [A]-itself.
The most resources-efficient approach to this is to properly configure the infrastructure tools for doing exactly this, without re-inventing wheel and/or using a fragile and performance-devastating scaffolding code(s).
So:
on the client-[*] side best use the below sketched primitive agent-concept. More complex setups, like using as clever event-handling facilities as Tkinter has evolved to have packed into the .mainloop() soft-real-time system, are better, yet it is not as easy to start design-battles on more than one front, so let's rather keep things simpler at this moment:
zmq_VERSION = zmq.zmq_version_info()
anAgentsIDENTITY = whateverHashOrHumanReadableSTRING
notMINE = anAgentsIDENTITY
if zmq_VERSION[0] < 4:
print "ZMQ{0:} ver < than expected, will exit".format( zmq_VERSION )
aCTX = zmq.Context( 2 ) # if performance boosting is needed
#SUB ---------------------------------------------------------------------------
aSUB = aCTX.socket( zmq.SUB )
aSUB.setsockopt( zmq.LINGER, 0 ) # protect your agent
aSUB.setsockopt( zmq.MAXMSGSIZE, m ) # protect your agent from DoS
aSUB.setsockopt( zmq.AFFINITY, 1 ) # protect your server resources
aSUB.setsockopt( zmq.HEARTBEAT_IVL, ivl ) # set server helping Heartbeats
aSUB.setsockopt( zmq.HEARTBEAT_TTL, ttl ) # set server helping Heartbeats
aSUB.setsockopt( zmq.INVERT_MATCHING, 1 ) # avoid server sending data back
aSUB.setsockopt( zmq.SUBSCRIBE, notMINE ) # NEVER .recv()-s data back
...
#SUB PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
aSUB.connect( "tcp://localhost:5557" )
#PUSH --------------------------------------------------------------------------
aPUSH = aCTX.socket( zmq.PUSH )
...
#PUSH PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
#main loop ---------------------------------------------------------------------
pass; notSoftFLAG = True; anAgentSignsWithIdentityPREFIX = anAgentsIDENTITY
while notSoftFLAG:
if aReasonToSendSomethingToServer:
aPUSH.send( anAgentSignsWithIdentityPREFIX
+ ":::"
+ aMsgPAYLOAD,
zmq.DONTWAIT
) # inspect ZMQError
...
pass
if aSUB.poll( 100 ):
message = aSUB.recv( zmq.DONTWAIT ) # NEVER .recv()-s own data back
...
pass
if aReasonToFlagLoopEXIT:
notSoftFLAG = False
...
pass
if ...:
...
pass
#main loop ---------------------------------------------------------------------
pass
#########
# ALWAYS:
# better using context-aware try:/except:/finally:
aRetCODE = [ aSOCK.close() for aSOCK in ( aSUB, aPUSH, ) ]
...
aCTX.term()
# .term()
#########
all being well tuned inside the ZeroMQ infrastructure:
pass; zmq_VERSION = zmq.zmq_version_info()
if zmq_VERSION[0] < 4:
print "ZMQ{0:} ver < than expected, will exit".format( zmq_VERSION )
aCTX = zmq.Context( 2 ) # if performance boosting is needed
#PUB ---------------------------------------------------------------------------
aPUB = aCTX.socket( zmq.PUB )
aPUB.setsockopt( zmq.LINGER, 0 ) # protect your server
aPUB.setsockopt( zmq.MAXMSGSIZE, m ) # protect your server from DoS
aPUB.setsockopt( zmq.AFFINITY, 3 ) # protect your server resources
aPUB.setsockopt( zmq.HEARTBEAT_IVL, ivl ) # server L3-helper Heartbeats
aPUB.setsockopt( zmq.HEARTBEAT_TTL, ttl ) # server L3-helper Heartbeats
aPUB.setsockopt( zmq.INVERT_MATCHING, 1 ) # avoid server sending data back
aPUB.setsockopt( zmq.IMMEDIATE, 1 ) # avoid Queueing for dead-ends
aPUB.setsockopt( zmq.TOS, tos ) # allow for L3-router TOS-policies
...
#PUB PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
aPUB.bind( "tcp://*:5557" ) # expose AccessPoint on tcp://
#PULL --------------------------------------------------------------------------
aPULL = aCTX.socket( zmq.PULL )
aPULL.setsockopt( zmq.LINGER, 0 ) # protect your server
aPULL.setsockopt( zmq.MAXMSGSIZE, m ) # protect your server from DoS
aPULL.setsockopt( zmq.AFFINITY, 3 ) # protect your server resources
aPULL.setsockopt( zmq.HEARTBEAT_IVL, ivl )# server L3-helper Heartbeats
aPULL.setsockopt( zmq.HEARTBEAT_TTL, ttl )# server L3-helper Heartbeats
...
#PULL PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
aPULL.bind( "tcp://*:5558" ) # expose AccessPoint on tcp://
...
#main loop ---------------------------------------------------------------------
pass; notSoftFLAG = True
while notSoftFLAG:
NOP_SLEEP = 10 # set a 10 [ms] sleep in case NOP
if aPULL.poll( 0 ): # NEVER block/wait
aMSG = aPULL.recv( zmq.DONTWAIT ) # NEVER .recv()-s own data back
#CPY = zmq_msg_copy( &aMSG ); // WARNING ABOUT NATIVE C-API
# // HANDLING, NEED .COPY()
# // NEED .CLOSE()
aPUB.send( aMSG, zmq.DONTWAIT ) # re-PUB-lish to all others but sender
...< process aMSG payload on server-side, if needed >...
NOP_SLEEP = 0 # !NOP, avoid 10[ms] NOP-loop sleep
pass
if aReasonToFlagLoopEXIT:
notSoftFLAG = False
...
NOP_SLEEP = 0
pass
if ...:
...
pass
sleep( NOP_SLEEP ) # a soft-real-time controlled sleep on NOP
#main loop ---------------------------------------------------------------------
pass
#########
# ALWAYS:
# better using context-aware try:/except:/finally:
aRetCODE = [ aSOCK.close() for aSOCK in ( aPUB, aPULL, ) ]
...
aCTX.term()
# .term()
#########