Python ZMQ responder not receiving message
Asked Answered
G

2

6

I am trying a simple zmq script but somehow the responder is not getting the first message.

The Responder looks like this:

def main():
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.connect("tcp://localhost:{}".format(5560))
    print("connected ...")
    while True:
          #  Wait for next request from client
          message = socket.recv_pyobj()
          #print (message)
          print(message)
if __name__ == '__main__':
    main()

I am sending the request with the following code from another process:

def main():
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.connect("tcp://localhost:{}".format(5560))
    print("sending object")
    socket.send_pyobj("ok")
    print("done")

if __name__ == '__main__':
    main()

Does anybody have an idea why it does not arrive?

Glissando answered 11/8, 2019 at 16:35 Comment(0)
D
1

You must add .bind() an IP address in your REP code snippet instead of .connect(). In REP/REQ pattern there are the request and response factor, so you can throw feedback in the responder code. Thus your code will be as follows:

Responder:

import zmq

def main():
    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind("tcp://127.0.0.1:5560")
    while True:
        message = socket.recv_pyobj()
        print(message)
        socket.send_string('Your message received.')

if __name__ == '__main__':
    main()

Request code snippet:

import zmq

def main():
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.connect("tcp://localhost:5560")
    print("sending object")
    socket.send_pyobj("ok")
    message = socket.recv()
    print(message)

if __name__ == '__main__':
    main()

Out (req):

sending object
b'Your message received.'

Out (rep):

ok

[NOTE]:

  • If you want to send a simple string without any response, using the PUSH/PULL or SUB/PUB pattern is more suitable instead of using REP/REQ and also you could use the socket.send_string('ok') instead of socket.send_pyobj('ok') in request section and socket.recv() instead of socket.recv_pyobj() in responder.

  • Note that in .bind() case, you shouldn't use the localhost string Relevant Post.

Dewan answered 14/8, 2019 at 5:52 Comment(0)
R
-1

Q : Does anybody have an idea why it does not arrive?

Oh sure I have.

There are a few principal points that govern how the ZeroMQ-based distributed-computing systems may and do work.

If you are new to using ZeroMQ or other its derivatives ( et al ), be sure not to miss Pieter Hintjen's must read book "Code Connected. Volume 1".

There are two places, where missed (or principally undeliverable) messages may come:

  • Not yet ready process, which is assumed to receive a message, being the first
  • Not available resource (port) for a successful .bind(), being the second

Harsh network transport conditions related problems are not the case for localhost-only ( vmci://-virtualised of internal port-abstracted network ) experimentations


Curable:

def main():
    context = zmq.Context()
    socket = context.socket( zmq.REQ )
    socket.setsockopt(       zmq.LINGER,    0 )              # ALWAYS PREVENT BLOCKING
    socket.setsockopt(       zmq.IMMEDIATE, 1 )              # BLOCK UNTIL CONN-READY
    #ocket.setsockpt(        zmq.ZMQ_HANDSHAKE_IVL, ... )    # IF TWEAKING NETWORK-WIDE
    # OR:
    # a stone-age wait for the other part get started in a one-computer demo:
    # sleep( 20 )
    # :o)
    socket.connect(         "tcp://localhost:{}".format( 5560 ) )
    print(                  "Will try to dispatch an object to Context() instance" )
    socket.send_pyobj(      "ok" )
    print(                  ".send() method has returned from a blocking-call mode" )
    ...
    #--------------------------------------------------------# ALWAYS
    socket.close()                                           # ALWAYS RELEASE RESOURCES
    context.term()                                           # ALWAYS RELEASE RESOURCES
    #                                                        # ALWAYS (not all versions
    #                                                        #         have "friendly"
    #                                                        #         defeaults to rely
    #                                                        #         on others,
    #                                                        #         so be explicit)
    #--------------------------------------------------------# ALWAYS

One side, obviously not necessarily the REP, yet here it fits better, due to while, must .bind(), the other(s) just .connect() to a known connection target:

def main():
    context = zmq.Context()
    socket = context.socket( zmq.REP )
    socket.setsockopt(       zmq.LINGER,    0 )              # ALWAYS PREVENT BLOCKING
    socket.setsockopt(       zmq.IMMEDIATE, 1 )              # BLOCK UNTIL CONN-READY
    #ocket.setsockpt(        zmq.ZMQ_HANDSHAKE_IVL, ... )    # IF TWEAKING NETWORK-WIDE
    socket.bind(            "tcp://localhost:{}".format( 5560 ) )
    print(                  ".bind() ...")
    try:
        while True:             # Wait for next request from client:
              message = socket.recv_pyobj()
              print( message )
    except:
        print( "EXC'd" )
    finally:
        #----------------------------------------------------# ALWAYS
        socket.unbind( "tcp://localhost:{}".format( 5560 ) ) # ALWAYS RELEASE PORT
        socket.close()                                       # ALWAYS RELEASE RESOURCES
        context.term()                                       # ALWAYS RELEASE RESOURCES
        #                                                    # ALWAYS (not all versions
        #                                                    #         have "friendly"
        #                                                    #         defeaults to rely
        #                                                    #         on others,
        #                                                    #         so be explicit)
        #----------------------------------------------------# ALWAYS

Last but not least, this will start to work, yet it will hang in an infinite waiting, due to a perhaps missed principle of the REQ/REP-behaviour archetype. One must ASK ( REQ.send()-s ), the other one, who is to REPLY has to listen-up the question REP.recv(), however it also has to ANSWER... REP.send("something") before we may move forwards in two-step-tango for two and the ASKER has also to get the answer listened to REQ.recv().

Then and only then the ASKER can send another question by another REQ.send().

So, both your sending REQ-parts, yet mainly the receiving REP-part, inside infinite while True:{...} loop has to get revised, in order to receive any second and further messages, even for cases when REQ-s die after a single shot and never listen to any answer from REP.

Rider answered 12/8, 2019 at 10:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.