skip to Main Content

I’m testing ZeroMQ for PHP. My goal is to send messages to a Python script.
Everything works fine if i launch my transmission script from PHP cli

php /path/to/myscript.php

while it fails if it’s a web request. I’ve tried executing the server script from PHP cli as above (which seems to be the more logical way) and with a web request.

I’ve got a Centos 7 server with PHP 7.2 and ZeroMQ 1.1.3 installed through PECL install.

I even tried launching the above command with shell_exec/exec inside the client script but it doesn’t work. Connection works fine, but it doesn’t send nor receive.

Client code:

$context = new ZMQContext();

//  Socket to talk to server
echo "Connecting to hello world server...n";
$requester = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$currentObject = $requester->connect("tcp://localhost:5555");


for ($request_nbr = 0; $request_nbr != 10; $request_nbr++) {
    printf ("Sending request %d...n", $request_nbr);
    $risSend = $requester->send("Hello", ZMQ::MODE_NOBLOCK);
    print_r($risSend);
    $reply = $requester->recv();
    printf ("Received reply %d: [%s]n", $request_nbr, $reply);
}

Server Code:

$context = new ZMQContext(1);

//  Socket to talk to clients
$responder = new ZMQSocket($context, ZMQ::SOCKET_REP);
$responder->bind("tcp://*:5555");

while (true) {
    //  Wait for next request from client
    $request = $responder->recv();

    printf ("Received request: [%s]n", $request);

    //  Send reply back to client
    $responder->send("World");
}

The browser gets stuck, without any error. Even using a timeout it reaches the limit and exits but I can’t get any error message.

2

Answers


  1. Chosen as BEST ANSWER

    Ok, i finally found the solution. Thanks to @user3666197 i managed to get an error. And it was a "permission denied" error.

    With CentOS (and probably all linux sytems with SELinux) you gotta add a rule for the port used by ZMQ for the webserver.

    Example:

    semanage port -a -t http_port_t -p tcp 5555
    

  2. 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 maden";
          }
    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 detailsn";
            } 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 maden";
          }
    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 detailsn";
            } 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 maden";
            }
        } 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 detailsn";
            } 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() ) )
    

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search