简体   繁体   中英

ZeroMQ socket Recv() throws 'Context was terminated' exception - why and how to recover?

Using a ZMQ.SocketType.REP (reply) messaging socket with ZeroMQ, I am receiving messages and then sending an "OK" message back.

For now, I'm trying this locally (sending/receiving messages from the same C# console app running on the same machine).

Fairly regularly (after about 1500 messages), the line:

var receivedBytes = _recvSocket.Recv();

... will throw an exception: Context was terminated

My question is, why does this happen, and how do you recover from it ?

I have a System.Threading.Thread dedicated to running my "server-side" ZeroMQ reply socket, here is the loop that it runs:

    private static void MessagingLoopReceive(object state)
    {
        if (_zmqc == null)
        {
            _zmqc = new ZMQ.Context(1);
        }

        _recvSocket = _zmqc.Socket(ZMQ.SocketType.REP);
        _recvSocket.Bind("tcp://*:5556");

        while (true)
        {
            if (_queueStop)
            {
                break;
            }

            //Console.WriteLine("Server blocking for receive...");
            var receivedBytes = _recvSocket.Recv();

            if (receivedBytes != null && receivedBytes.Length > 0)
            {
                //Console.WriteLine("Server message received from client, sending OK");
                _recvSocket.Send("OK", Encoding.ASCII);
                //Console.WriteLine("Server OK sent, adding message to queue");
                _queuedMessages.Enqueue(receivedBytes);
            }
            else
            {
                Thread.Sleep(1);
            }
        }
    }

这意味着有人(垃圾收集器?)关闭了上下文。

Adding this answer for completeness.

    if (_zmqc == null)
    {
        _zmqc = new ZMQ.Context(1);
    }
    _recvSocket = _zmqc.Socket(ZMQ.SocketType.REP);

This is the only place the context _zmqc is used. Therefore the GC sees it as a potential candidate for cleaning up. Once it is, dispose is called on it. This will then force all sockets to terminate with a ETERM error number from ZMQ.

The first thing that needs to be done is use a using clause around where the context needs to stay alive. This will force the GC to leave the object alone.

Next when closing down the loop you need to catch the exception see that its a ETERM error number and then exit gracefully.

try
{
    //Any ZMQ socket sending or receiving 
}
catch (Exception e)
{
    if (e.Errno == ETERM)
    {
        //Catch a termination error. 
        break; // or return, or end your loop 
    }
}

Enjoy!

Edit: Almost forgot error numbers in ZMQ are "fancy" the first one starts at 156384712 and they go up. ETERM is 156384712 + 53. However just the doco as this may have changed since writing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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