简体   繁体   English

ZMQ:socket_send / recv阻止

[英]ZMQ: socket_send/recv blocking

So I'm trying to set up some simple communication between ZMQ in Python and ZMQ in a C/C++ extension. 因此,我试图在Python中的ZMQ和C / C ++扩展中的ZMQ之间建立一些简单的通信。 Python sets up the context, binds an inproc socket, and passes the context and socket name to the extension. Python设置上下文,绑定一个inproc套接字,并将上下文和套接字名称传递给扩展名。 The extension sets up its own socket, connects, and listens for messages. 该扩展程序会设置自己的套接字,进行连接并侦听消息。 Python then sends a header and then the string representation of a dictionary over to the extension. 然后,Python发送一个标头,然后发送字典的字符串表示形式到扩展名。 Pretty simple stuff using REQ/REP sockets. 使用REQ / REP套接字的非常简单的东西。 However, for some reason I can't seem to find, the call to socket.send is blocking, and the extension never gets past the call to zmq_recv. 但是,由于某种原因,我似乎找不到,对socket.send的调用被阻止,并且该扩展永远不会超过对zmq_recv的调用。 I have a test environment where I have almost exactly the same scenario play out, but the sockets don't block, and I've triple-checked the code and it should be working in the same way. 我有一个测试环境,可以在几乎完全相同的情况下进行测试,但是套接字不会阻塞,并且我对代码进行了三遍检查,并且它应该以相同的方式工作。

PYTHON: 蟒蛇:

import zmq
import cppextension
# No lectures about using threading please. I'm restricted to, in essence
# using this function because of the code base I'm working with.
from thread import start_new_thread

socket = self.zmq_context.socket(zmq.REQ)
socket_name = "inproc://agl"
socket.bind(socket_name)
t = start_native_thread(cppextension.actor,
                        (self.zmq_context, socket_name))

test_send = {"foo": 1, "bar": 2}
# BLOCKS ON THIS LINE VVVVV
socket.send("TEST", flags=zmq.SNDMORE)
socket.send(str(test_send))
socket.recv()
socket.send("STOP")

C/C++: C / C ++:

// Originally these used std::basic_string<Py_UNICODE> but I reverted
// back to normal std::string so I can use a JSON parsing library.
typedef string pystring;
typedef char pystring_t;

extern "C" PyObject *
actor(PyObject *self, PyObject *args) {
    PyObject *py_context, *py_connect_to;
    PyThreadState *_save;
    void *context;
    char *connect_to;
    void *socket;
    int rc;

    if(!PyArg_ParseTuple(args, "OO", &py_context, &py_connect_to)) {
        PyErr_SetString(PyExc_TypeError, "Expected two arguments (ZMQ context, name of socket to connect to)");
        return NULL;
    }
    py_context = PyObject_GetAttrString(py_context, "_handle");
    if(py_context == NULL) {
        PyErr_SetString(PyExc_TypeError, "Could not get '_handle' from context");
        return NULL;
    }
    if(!PyInt_Check(py_context)) {
        PyErr_SetString(PyExc_TypeError, "_handle was not an integer");
        return NULL;
    }
    context = (void*)PyInt_AsLong(py_context);
    connect_to = new char[PyString_Size(py_connect_to) + 1];
    strcpy(connect_to, PyString_AsString(py_connect_to));
    _save = PyEval_SaveThread();

    //
    // GIL-less operation BEGIN
    // ** WARNING: Do NOT call any functions that begin with 'Py', or touch any
    //    data structures that begin with 'Py' while in this section. It *WILL*
    //    blow up the Python interpreter.
    //
    socket = zmq_socket(context, ZMQ_REP);
    rc = zmq_connect(socket, connect_to);

    pystring TEST("TEST");
    pystring STOP("STOP");
    pystring SUCCESS("SUCCESS");
    pystring FAILURE("FAILURE");

    if(rc == 0) {
        int going = 1;
        // Should be able to hold a full megabyte of text, which should be enough
        // for any message being passed in.
        // Is there a way to query size of the incoming message...?
        char buffer[1000000];
        while(going) {
            // BLOCKS ON THIS LINE VVVVVV
            int size = zmq_recv(socket, buffer, 1000000, 0);
            if(size == -1) {
                // ERROR
                continue;
            }
            // Assume we don't get larger than 1MB of data. Should put a
            // check around this at some point, but not right now.
            buffer[size] = 0;

            pystring fullmsg(buffer);
            cout << "ZMQ RECIEVED: " << fullmsg << endl;
            if(fullmsg == TEST) {
                size = zmq_recv(socket, &buffer, 1000000, 0);
                if(size != -1) {
                    buffer[size] = 0;
                    pystring json_fullmsg(buffer);
                    cout << "ZMQ JSON: " << json_fullmsg << endl;
                    contacts.add(json_fullmsg);
                    zmq_send(socket, SUCCESS.c_str(), SUCCESS.size() + 1, 0);
                }
                else {
                    zmq_send(socket, FAILURE.c_str(), FAILURE.size() + 1, 0);
                }
            }
            else if(fullmsg == STOP) {
                going = 0;
                zmq_send(socket, SUCCESS.c_str(), SUCCESS.size() + 1, 0);
            }
        }
    }
    else {
        // ERROR
        int err = zmq_errno();
        switch(err) {
        case EINVAL:
            cout << "ZMQ CONNECT ERR: " << "Endpoint supplied is invalid" << endl;
            break;
        default:
            cout << "ZMQ CONNECT ERR: " << err << endl;
            break;
        }
    }
    zmq_close(socket);
    //
    // GIL-less operation END
    //

    PyEval_RestoreThread(_save);
    Py_INCREF(Py_None);
    return Py_None;
}

Any help figuring out what's going on here is very appreciated. 非常感谢您弄清楚这里发生的事情。

EDIT: Also note that this code will be running in an environment where gevent has monkeypatched the standard library. 编辑:还请注意,此代码将在gevent猴子修补了标准库的环境中运行。 This is part of the reason I'm using thread.start_new_thread, because it was saved before the monkeypatching happened, and I want a real thread not a green thread. 这是我使用thread.start_new_thread的部分原因,因为它是在monkeypatching发生之前保存的,并且我想要一个真正的线程而不是绿色线程。

two things, 两件事情,

because you are using req/rep in your modified version, the "send,send,recv,send..." will not work. 因为您在修改版本中使用req / rep,所以“发送,发送,接收,发送...”将不起作用。 both the send/recv MUST work in 'lock-step' manner (send,recv,send,revc.) 发送/接收都必须以“锁定步骤”方式工作(发送,接收,发送,接收)。

the ZMQ_NOBLOCK will raise an exception of EAGAIN, which may mean "the socket connection is not complete, please come back later." ZMQ_NOBLOCK将引发EAGAIN异常,这可能意味着“套接字连接未完成,请稍后再回来。” try placing a timer/sleep after bind and both send/recv. 尝试在绑定和发送/接收后都设置计时器/睡眠状态。 this is what's causing the "Resource temporarily unavailable" msg. 这就是导致“资源暂时不可用”消息的原因。

hope this helps 希望这可以帮助

mr. 先生。 onoffon onoffon

Not sure this would help, but here goes: 不确定这是否有帮助,但是这里有:

  • Are you sure that send() blocks, and it is not simply a matter of never getting the reply? 您确定send()阻塞,这不仅仅是从不获得答复的问题? You could call send() with ZMQ_NOBLOCK and see if an exception is raised. 您可以使用ZMQ_NOBLOCK调用send()并查看是否引发异常。 If it is, then indeed send() is failing to enqueue the message. 如果是,则send()确实无法使消息入队。
  • Have you considered PAIR sockets instead of REQ/REP ? 您是否考虑过PAIR插座而不是REQ/REP The guide recommends this for inproc communication between threads. 本指南建议在线程之间进行inproc通信时使用此方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM