C++ ZMQ:套接字_发送/接收阻塞

C++ ZMQ:套接字_发送/接收阻塞,c++,python,python-2.7,zeromq,pyzmq,C++,Python,Python 2.7,Zeromq,Pyzmq,所以我试图在Python中的ZMQ和C/C++扩展中的ZMQ之间建立一些简单的通信。Python设置上下文,绑定inproc套接字,并将上下文和套接字名称传递给扩展。扩展设置自己的套接字、连接和侦听消息。Python然后向扩展发送一个头,然后是字典的字符串表示。使用REQ/REP套接字非常简单。然而,由于某种原因,我似乎找不到,对socket.send的调用被阻塞了,扩展从未通过对zmq_recv的调用。我有一个测试环境,在那里我有几乎完全相同的场景播放,但是套接字不会阻塞,我已经三次检查了代码

所以我试图在Python中的ZMQ和C/C++扩展中的ZMQ之间建立一些简单的通信。Python设置上下文,绑定inproc套接字,并将上下文和套接字名称传递给扩展。扩展设置自己的套接字、连接和侦听消息。Python然后向扩展发送一个头,然后是字典的字符串表示。使用REQ/REP套接字非常简单。然而,由于某种原因,我似乎找不到,对socket.send的调用被阻塞了,扩展从未通过对zmq_recv的调用。我有一个测试环境,在那里我有几乎完全相同的场景播放,但是套接字不会阻塞,我已经三次检查了代码,它应该以相同的方式工作

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++:

// 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;
}
//最初使用的是std::basic_字符串,但我恢复了
//回到正常的std::string,这样我就可以使用JSON解析库了。
typedef字符串pystring;
typedef char pystring_t;
外部“C”PyObject*
参与者(PyObject*self,PyObject*args){
PyObject*py_context,*py_connect_to;
PyThreadState*\u保存;
无效*上下文;
字符*连接到;
空*插座;
int rc;
if(!PyArg\u语法元组(args,“OO”、&py\u上下文、&py\u连接到)){
PyErr_SetString(PyExc_TypeError,“需要两个参数(ZMQ上下文,要连接到的套接字的名称)”;
返回NULL;
}
py_context=PyObject_GetAttrString(py_context,“_handle”);
if(py_context==NULL){
PyErr_SetString(PyExc_TypeError,“无法从上下文获取”_handle”);
返回NULL;
}
如果(!PyInt_检查(py_上下文)){
PyErr_SetString(PyExc_TypeError,“_handle不是整数”);
返回NULL;
}
上下文=(void*)PyInt_AsLong(py_上下文);
connect_to=新字符[PyString_Size(py_connect_to)+1];
strcpy(connect_to,PyString_AsString(py_connect_to));
_save=PyEval_SaveThread();
//
//无GIL操作开始
//**警告:不要调用任何以“Py”开头的函数,也不要触摸任何
//在本节中以“Py”开头的数据结构。它*将*
//炸毁Python解释器。
//
socket=zmq_socket(上下文,zmq_REP);
rc=zmq_连接(插座,连接到);
pystring测试(“测试”);
pystring STOP(“停止”);
pystring成功(“成功”);
pystring故障(“故障”);
如果(rc==0){
int=1;
//应该能够保存一个完整的兆字节的文本,这应该足够了
//对于传入的任何消息。
//有没有办法查询传入消息的大小。。。?
字符缓冲区[1000000];
边走边{
//这条线路上的闭塞物
int size=zmq_recv(套接字,缓冲区,1000000,0);
如果(大小==-1){
//错误
继续;
}
//假设我们没有获得超过1MB的数据。应该放一个
//在某个时候检查一下,但不是现在。
缓冲区[大小]=0;
pystring fullmsg(缓冲区);

不能肯定这会有帮助,但下面是:

  • 您确定
    send()
    阻塞了吗?这不仅仅是一个永远无法得到回复的问题?您可以使用调用
    send()
    ,查看是否引发异常。如果是,则确实
    send()
    无法将消息排队
  • 对于线程之间的
    inproc
    通信,您是否考虑过使用
    PAIR
    套接字而不是
    REQ/REP
  • 有两件事

    由于您在修改的版本中使用req/rep,“发送、发送、接收、发送…”将 不工作。发送/接收必须以“锁定步进”方式工作(发送、接收、发送、revc.)

    ZMQ_NOBLOCK将引发EAGAIN异常,这可能意味着“套接字连接” 未完成,请稍后返回。“尝试在绑定后放置计时器/睡眠,并同时放置两者 发送/接收。这是导致“资源暂时不可用”消息的原因

    希望这有帮助


    Onofon先生

    并不是说它解决了您的问题,而是Python使用“#”作为注释,而不是“/”.Doh!可能是在我添加这些注释时用C/C++的术语思考的。谢谢,修复了这些注释。它们使用的是相同的zmqlib版本吗?pythons zmqlib是由pip编译的扩展吗?它不是由pip编译的,我们自己构建了libzmq和pyzmq。Python和C/C++代码中都使用了相同的版本。事实上,我甚至无法导入导入zmq之前,我会收到一个ImportError,抱怨找不到符号(zmq_socket等)由于未加载库:)如果改用
    tcp:
    transport,是否会遇到同样的问题?
    inproc
    要求在尝试连接之前完成绑定,而
    tcp
    更具容错性。使用socket.send命令并使用zmq.DONTWAIT作为标志,我会得到“资源暂时不可用”打印到控制台。稍微改变一下,Python使用REP,C/C++使用REQ,然后在发送测试数据之前接收一条消息,我得到“连接被拒绝”。我在C/C++端看到了EAGAIN和EFSM错误。@Freezeburn。好的,那对套接字呢?不幸的是,它们似乎没有做任何事情。它的行为仍然与我使用REQ/REP套接字时的行为完全相同。