ZMQ连接的清理及其context部件的关闭
一开始使用jeroMQ,由于java会自动回收资源,所以socket对象及context对象的清理比较简单。断开连接和关闭连接不需要考虑连接的状态。所以程序比较简单。
但在C/C++环境下,zmq连接的处理需要考虑如下情况:
(1)在zmq_recv()的阻塞状态下,不可能断开连接。
(2)在连接没有断开和关闭的情况下,context不能关闭(呈阻塞状态)。
(3)在上面两种情况下,可能会有内存泄漏的问题。
为了解决以上问题,在C/C++环境下,必须:
(1)如果没有把握保证zmq_recv()一定能够接收到消息,则必须设置其ZMQ_RCVTIMEO值,使其在接不到消息时可以退出阻塞,以判断是否程序需要退出。
int recvTime = 500; zmq_setsockopt(_socket, ZMQ_RCVTIMEO, &recvTime, sizeof(recvTime));
ret1 = zmq_recv(_zsocket, sub, 128, 0); //接收消息 if(ret1 == -1) { int error = zmq_errno(); cout << "ERROR: messageReceiver socket: wrong message --" << zmq_strerror(error) << endl; if(getSimEnd()) { //closedeliver(); //stop deliver closeCallbackLink(); //stop the callback link cout << "messageReceiver thread closed......2" << endl; return; } }
void MessageReceiver::closeCallbackLink() { //QMutexLocker locker(&_mutex); if(_zsocket == nullptr) return; //setSimEnd(); //Sleep(500); if(_type == tcp) { char abuf[128]; memset(abuf, '\0', 128*sizeof(char)); sprintf_s(abuf, 128*sizeof(char), "tcp://%s:%d", _rtiHost.c_str(), fport); zmq_disconnect(this->_zsocket, abuf); zmq_close(_zsocket); _zsocket = NULL; return; } if(_type == ipc) { char abuf[128]; memset(abuf, '\0', 128*sizeof(char)); sprintf_s(abuf, 128*sizeof(char), "ipc://%s", _rtiIpcAddress.c_str()); zmq_disconnect(this->_zsocket, abuf); zmq_close(_zsocket); _zsocket = nullptr; return; } }
(2)在退出阻塞时,要判定是否程序需要退出,如果不退出,则再调用zmq_recv()接收消息,如此循环。
(3)为了加快断开和关闭连接的速度,设置有限的ZMQ_LINGER值,使连接快速断开。
int linger = 0; zmq_setsockopt(_socket, ZMQ_LINGER, &linger, sizeof(int64_t));
(4)在(1)中判定需要退出时,应当断开连接和关闭连接,释放socket。
(5)要想关闭context,必须关闭其上的所有socket,否则关闭context时会阻塞,导致程序无法退出。
(6)如果连接在线程内,则退出线程和关闭连接尽量同时完成。
在一个程序中,我在一个context上建立了两个连接,分别被两个线程使用,断开连接并关闭socket后,发现关闭context时总是阻塞,百思不得其解。后来偶然发现,在建立其中一个连接时,使用了zmq_socket_monitor建立了一个监视连接,这个sokcet也是建立在上面的context上的,豁然开朗。将该连接断开并将其socket关闭后,这个context被顺利关闭。