OBSERVATION : The browser gets stuck, without any error.
This is pretty legal state. For it to happen, it is quite enough to "miss" the arrival of the first REQ
-side-already dispatched request and due to a pleasure do depend on a distributed-Finite-State-Automaton, we fall into an unsalvageable dead-lock, where the REQ
-side waits for an answer, that will never arrive (see next) and the REP
-side waits for a request, that will never arrive (see the REQ
-side already waiting ) and such a state remains forever that.
A best next step:
In case one has never worked with ZeroMQ,
or have never met the concept of the art of Zen-of-Zero,
one may here enjoy to first look at "ZeroMQ Principles in less than Five Seconds" before diving into further details
Start
with unconditionally working archetypes - a pair of PUSH / PULL
simplex-channels, that do not require a dFSA-two-step of REQ-REP-REQ-REP-REQ-REP-...-{deadlock}
... a principally unavoidable terminal state, about which one is just never sure when it happens, but it will ... at some later time :o)
Next,
may increase a robustness of the message-flow, using zmq_setsockopt( ZMQ_IMMEDIATE, 1 )
that avoids moving messages onto incomplete connections between / among peers.
Always
prefer non-blocking forms of .recv()
-methods, best with a pre-test of a message-presence with a .poll()
-method. Poller
-class, while available in many language-bindings is not always as handy and as flexible as using explicit .poll()
-method directly on a Socket
-instance.
Also feel free to read more about fine-tuning the ZeroMQ tools and other implications of the Art of the Zen-of-Zero here.
A Server-side mock-up: As a { PASS | FAIL }-proof of .send()---.recv()
-delivery chain works?
<?php /* Create new PUSH-ing end */
$aCTX = new ZMQContext();
try { /* Try: things may turn wreck havoc */
$PUSHer = $aCTX->getSocket(, ZMQ::SOCKET_PUSH );
echo "POSACK'd: .getSocket() was made\n";
}
catch ( ZMQSocketException $e ){
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else {
die( " - Get ERR: " . $e->getMessage() );
}
}
try { /* Try: things may turn wreck havoc */
$PUSHer->bind( "tcp://A.B.C.D:NNN" ); /* IP address to .connect() */
echo "POSACK'd: .bind() was made\n";
}
catch ( ZMQSocketException $e ){
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else {
die( " - Get ERR: " . $e->getMessage() );
}
}
$retries = 1234567;
do { /* Start a loop */
try { /* Try: to PUSH.send() */
echo "Trying to send a message #" . ( 1234568 - $retries ) . "\n";
$PUSHer->send( "This is a message", ZMQ::MODE_DONTWAIT );
echo "POSACK'd: PUSHer.send() was made\n";
}
} catch ( ZMQSocketException $e ) {
echo " NACK'd: I told you ...\n"; /* Handle with care ... */
if ( $e->getCode() === ZMQ::ERR_... ) {
echo " - Got ERR_..., read ZeroMQ API documentation for details\n";
} else { /* For all ERR_... states */
die( " - Got ERR_...: " . $e->getMessage() );
}
}
/* --------------------------------------------------------------------
Here one may add an attempt to .recv( $PULLer, ZMQ::MODE_DONTWAIT );
and test for a non-empty string returned
-------------------------------------------------------------------- */
usleep( 1 ); /* Sleep a bit between operations */
} while ( --$retries );
?>
Client-side mock-up, to test the PUSH-er lives and .send()
-s
import time, datetime, zmq; print( "Thissssss Sssssssssssssssssssssssssssssssssssssssnake uses ZeroMQ ver:{0:}".format( zmq.__version__ ) )
aCtx = zmq.Context()
aPull= aCtx.Socket( zmq.PULL )
aPull.setsockopt( zmq.LINGER, 0 ) # always ... be explicit
aPull_address2c = "tcp://A.B.C.D:NNN"
M0 = "{0:} try a .connect( {1:} ), if it gets to PUSH-er side"
M1 = "{0:} try a .recv(), if it gets any message"
M2 = "{0:} got a .recv()->[[[ {1:} ]]]"
M3 = "{0:} EXC'd will gracefully release resources and terminate..."
M4 = "{0:} did"
try:
print( M0.format( datetime.datetime.isoformat( datetime.datetime.now() ),
aPull_address2c
)
)
aPull.connect( aPull_address2c );
while True:
print( M1.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
m = aPull.recv( zmq.NOBLOCK ) # always ... avoid blocking waits
if ( len( m ) > 0 ):
print( M2.format( datetime.datetime.isoformat( datetime.datetime.now() ),
str( m ) # always ... fused to str()
)
)
time.sleep( 5 )
else:
time.sleep( 1 )
pass
################################################################
# Here one may add an attempt to aPush.send( M4, zmq.NOBLOCK )
# and test if the reverse path aPush->$PULLer goes well
################################################################
except:
print( M3.format( datetime.datetime.isoformat( datetime.datetime.now() ) )
finally:
aPull.close() # always ... be explicit
aCtx.term() # always ... be explicit
print( M4.format( datetime.datetime.isoformat( datetime.datetime.now() ) )