Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send both image(ndarray) and string data in single ZMQ send request

For sending string data, following codes works :

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5667")
socket.send_string("my string data")

For sending image(ndarray) following code works :

def send_array(socket, img, flags=0, copy=True, track=False):
    """send a numpy array with metadata"""
    md = dict(
        dtype = str(img.dtype),
        shape = img.shape,
    )
    socket.send_json(md, flags|zmq.SNDMORE)
    return socket.send(img, flags, copy=copy, track=track)

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5667")
send_array(socket, my_ndarray_image )

But I do need to send both the string message along with the image file. Is there any way to append the message in the same request ?

Any ideas are welcomed ! Thanks

like image 912
Rohit Avatar asked Oct 29 '18 15:10

Rohit


1 Answers

I guess you are looking for multipart messages, which allow you to compose a message out of several frames. The Python implementation pyzmq already provides us with a nice wrapper for multipart messages. Here is an example for the server sending a multipart message:

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5555")

time.sleep(0.2)  # wait for socket to be properly bound

socket.send_multipart([b"first part", b"second part"])

And the client receiving a multipart message:

context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.setsockopt(zmq.SUBSCRIBE, b"")
socket.connect("tcp://localhost:5555")

print(socket.recv_multipart())

Please consider the following when working with multipart messages:

  • All the message parts will only ever be sent after you send the final part.
  • The receiver will always either receive all message parts or none at all.

In your specific example, you are already composing a multipart messages in the send_array function by using the flag zmq.SNDMORE. We can extend your example by also adding the string data with the zmq.SNDMORE flag. Here is the server side:

def send_array_and_str(socket, img, string, flags=0):
    md = dict(dtype = str(img.dtype), shape=img.shape)

    socket.send_string(string, flags | zmq.SNDMORE)
    socket.send_json(md, flags | zmq.SNDMORE)
    return socket.send(img, flags)

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:5667")
time.sleep(0.2)

my_ndarray = np.array([1, 2, 3])
my_string = "Hello World"
send_array_and_str(socket, my_ndarray, my_string)

And the client code receiving the message:

def recv_array_and_str(socket, flags=0, copy=True, track=False):
    string = socket.recv_string(flags=flags)
    md = socket.recv_json(flags=flags)
    msg = socket.recv(flags=flags, copy=copy, track=track)

    img = np.frombuffer(bytes(memoryview(msg)), dtype=md['dtype'])
    return string, img.reshape(md['shape'])

context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.setsockopt(zmq.SUBSCRIBE, b"")
socket.connect("tcp://localhost:5667")

print(recv_array_and_str(socket))

This code is based on the example Serializing messages with PyZMQ and adapted to work with Python 3. For Python 2, consider using buffer(msg) instead of bytes(memoryview(msg)).

like image 128
blu3r4y Avatar answered Sep 21 '22 07:09

blu3r4y