1
votes

It's a newbie question. I've got a very simple ZeroMQ server, written in Python. It's basically a remote procedure call, string in, string out.

import sys
import time
import zmq
import mutagen
import json

port = "64107"

context = zmq.Context()
responder = context.socket(zmq.REP)
try:
    responder.bind(f"tcp://*:{port}")
except:
    sys.exit(0)

print(f'running on port {port}')

try:

    while True:
        #  Wait for next request from client
        message = responder.recv()
        jsn = message.decode('utf8')
        rq = json.loads(jsn)

        reply = '{"reply": "unknown"}'

        if rq['request'] == 'settags':
            audio = mutagen.File(rq['file'], easy=True)
            if audio:
                reply = '{{"reply": "settags", "file": "{}", "tags": {{"tracknumber": "{}"}}}}'
                reply = reply.format(rq['file'], rq['tags']['tracknumber'])
                for tag, value in rq['tags'].items():
                    audio[tag] = value
                audio.save()
        elif rq['request'] == 'serve':
            reply = '{"reply": "serve"}'

        #  Send reply back to client
        responder.send_string(reply)

except KeyboardInterrupt as e:
    sys.exit(e)

I'd like to access this server from a Dart app. There is no ZeroMQ package for Dart, to my knowledge. Do I have to use JS zmq module? Is it mandatory to use ZeroMQ on the client side too? Maybe one can use Dart native io to communicate with this server?

Sorry, if the question is silly.

2

2 Answers

2
votes

Is it mandatory to use ZeroMQ on the client side too ?

Well, in case, you prefer to remain on the grounds of the comfort of the ZeroMQ smart abstractions, using the REQ/REP Scalable Formal Communications Archetype Pattern, the answer will be yes, unless you manage to implement the REQ/REP-behaviour, that will fully mimick == meet all the Archetype Pattern ZMTP-RFC-specification's requirements. So one can implement it on a green-field basis, without re-using any of the already available ZeroMQ library language bindings/wrappers.

Doable, yet sounds to me as a sort of a last resort.


There is also another approach available.

ZeroMQ can be setup to use a "raw"-socket archetype. This way you loose all the comfort of the ZeroMQ smart signalling and messaging abstractions, yet you may setup the ZeroMQ-side to "speak" over connections that run across the "raw"-(dumb)-sockets.


import sys
import time
import zmq
import mutagen
import json

PORT     = "64107"
TEMPLATE = ( '{{"reply":' + '"settags",'
           +   '"file":'  + '"{0:}",'                   # <-{0:} <- aFile
           +   '"tags":'  + '{{"tracknumber": "{1:}"}}' # <-{1:} <- aTrackNo
           +  '}}'
              )

pass;       context       = zmq.Context()
responder = context.socket( zmq.REP )
responder.setsockopt(       zmq.LINGER, 0 )

try:
    responder.bind( f"tcp://*:{PORT}" )
except:
    print( "EXC:: ZeroMQ-{0:} Error[{1:}]".format( zmq.pyzmq_version(),
                                                   zmq.ZMQError()
                                                   )
            )
    # --------------------------------------------------------------ALWAYS
    responder.close()
    context.term()
    # --------------------------------------------------------------ALWAYS
    sys.exit(0)                                   # EXIT --> --> -->

print( f'running on port {PORT}' )

try:
    while True:
        message = responder.recv()    # BLOCKS till next request from client
        rq = json.loads( message.decode( 'utf8' ) )

        if rq['request'] == 'settags':
            audio = mutagen.File( rq['file'], easy=True )
            if audio:
                for tag, value in rq['tags'].items():
                    audio[tag] = value
                audio.save()

                #esponder.send_json(   ...     ) # native .send_json() method
                responder.send_string( TEMPLATE.format( rq['file'],
                                                        rq['tags']['tracknumber']
                                                        )
                                       )
                continue                         # LOOP --^ --^ --^
            else:
                responder.send_string( '{"reply": "unknown"}' )
                continue                         # LOOP --^ --^ --^
        elif rq['request'] == 'serve':
                responder.send_string( '{"reply": "serve"}' )
                continue                         # LOOP --^ --^ --^

except KeyboardInterrupt as e:
    # --------------------------------------------------------------ALWAYS
    responder.close()
    context.term()
    # --------------------------------------------------------------ALWAYS
    sys.exit(e)                                  # EXIT --> --> -->
0
votes

I'd recommend solving this by writing a small ZeroMQ<->WebSocket adapter in python using tornado and then use WebSockets from a Dart client. WebSockets are supported natively in Dart and their message-based paradigm is compatible with ZeroMQ.