hangkongwang
hangkongwang

Reputation: 215

How can I adapt between non zmq socket and pyzmq?

I want to write a bridge adapt between non ZMQ socket and ZMQ socket.

client code:

import socket

if __name__ == '__main__':

    HOST = "localhost"
    PORT = 8888
    BUFFER = 4096

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print sock
        ret = sock.connect((HOST, PORT))
        print ret

        ret = sock.send('hello, tcpServer!')
        print ret
        recv = sock.recv(BUFFER)
        print ('[tcpServer siad]: %s' % recv)
        sock.close()
    except e:
        print e

proxy code, use this proxy to send request to ZMQ_REP server.

import zmq

if __name__ == '__main__':

    context = zmq.Context()
    socket = context.socket(zmq.STREAM)
    socket.bind("tcp://*:8888")

    socket_req = context.socket(zmq.REQ)
    socket_req.connect("tcp://localhost:5556")

    while True:
        clientid, message = socket.recv_multipart();

        print("id: %r" % clientid)
        print("request:",message.decode('utf8'))

        socket_req.send(clientid, flags=zmq.SNDMORE, copy=False)
        socket_req.send("Hi", copy=False)

        clientid, message = socket_req.recv_multipart()


        print("id: %r" % clientid)
        print("request:",message.decode('utf8'))

ZMQ_REP server code:

import zmq
import time
import sys


if __name__ == '__main__':

    port = '5556'
    if len(sys.argv) > 1:
        port = sys.argv[1]
        int(port)

    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind("tcp://*:%s" % port)

    while True:
        message = socket.recv()
        print "Received request: ", message
        time.sleep(1)
        socket.send("world from %s" % port)

the REQ get error:

Received request:  k
Traceback (most recent call last):
  File "req_server.py", line 21, in <module>
    socket.send("world from %s" % port)
  File "zmq/backend/cython/socket.pyx", line 574, in zmq.backend.cython.socket.Socket.send (zmq/backend/cython/socket.c:5434)
  File "zmq/backend/cython/socket.pyx", line 621, in zmq.backend.cython.socket.Socket.send (zmq/backend/cython/socket.c:5196)
  File "zmq/backend/cython/socket.pyx", line 181, in zmq.backend.cython.socket._send_copy (zmq/backend/cython/socket.c:2035)
  File "zmq/backend/cython/checkrc.pxd", line 21, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:6248)
zmq.error.ZMQError: Operation cannot be accomplished in current state

Upvotes: 4

Views: 1869

Answers (2)

minrk
minrk

Reputation: 38588

First point: It's generally not recommended to use REQ/REP in zmq. Use the more general DEALER/ROUTER combination. The only difference:

  1. When the ROUTER recvs, a routing ID is the first part of the message. This is used to route replies back to the sender.
  2. the lock-step req/rep/req/rep sequence is not enforced (this is the error you are seeing).

Here's a version of your proxy using DEALER:

import zmq

if __name__ == '__main__':

    context = zmq.Context()
    socket = context.socket(zmq.STREAM)
    socket.bind("tcp://*:8888")

    socket_req = context.socket(zmq.DEALER)
    socket_req.connect("tcp://localhost:5556")

    while True:
        clientid, message = socket.recv_multipart()

        print("id: %r" % clientid)
        print("request: %s" % message.decode('utf8'))

        socket_req.send(message)

        reply = socket_req.recv()
        print("reply: %s" % reply.decode('utf8'))

        socket.send_multipart([clientid, reply])

And your server, using ROUTER:

import zmq
import time
import sys


if __name__ == '__main__':

    port = 5556
    if len(sys.argv) > 1:
        port = int(sys.argv[1])

    context = zmq.Context()
    socket = context.socket(zmq.ROUTER)
    socket.bind("tcp://127.0.0.1:%i" % port)

    while True:
        message = socket.recv_multipart()
        req_id = message[0]
        print("Received request: %s" % message[1:])
        time.sleep(1)
        socket.send_multipart([req_id, "world from %s" % port])

Upvotes: 6

Jason
Jason

Reputation: 13766

socket_req.send(clientid, flags=zmq.SNDMORE, copy=False)
socket_req.send("Hi", copy=False)

Best guess is that it's not properly registering the SNDMORE flag and attempting to send a whole new request rather than appending to the first one (thus breaking the strict SEND/RECEIVE order for REQ sockets)... thus the "current state" of the socket would not allow it to send the second part of your message. Try using send_multipart(), or validating that your parameters are being passed in correctly.

Upvotes: 0

Related Questions