NetMQ / NetMQ3-x

NetMQ 3.x stable release branch - bug fixes only
Other
9 stars 17 forks source link

Exception from a background thread when socket is disposed while receiving a message #14

Open msykora opened 7 years ago

msykora commented 7 years ago

Hello,

I am using NetMQ 3.3.3.4 and this is crashing my whole application as it cannot be catched running poller async on a background thread. It happens when the other side closes their socket just as a message comes in, but also when a firewall blocks outgoing connections, etc. I think the socket should handle it gracefully and not throw an unhandled exception... Code to reproduce this easily(doesn't happen 100% o the time but most of the time, probably some timing issue, sometimes a nullreference is thrown instead):

        using (poller = new NetMQPoller()) {
            poller.RunAsync();
            using (NetMQSocket server = new RouterSocket(), client = new RouterSocket()) {
                var serverId = Encoding.UTF8.GetBytes("server");
                server.Options.Identity = serverId;
                server.Bind(address);
                server.ReceiveReady += (sender, revents) => {
                        var msg = revents.Socket.ReceiveMultipartBytes();
                        Console.WriteLine("msg received:" + Encoding.UTF8.GetString(msg[2]));
                    };
                poller.Add(server);

                client.Options.DelayAttachOnConnect = true;
                client.Connect(address);
                Console.WriteLine("sending: " + client.TrySendFrame(serverId, true));
                server.Dispose();
                Thread.Sleep(500);
                Console.WriteLine("all done, press enter to end");
                Console.ReadLine();
            }

Here is the exception: System.Net.Sockets.SocketException occurred HResult=0x80004005 Message=An existing connection was forcibly closed by the remote host Source=System StackTrace: at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) at NetMQ.Core.Mailbox.TryRecv(Int32 timeout, Command& command) at NetMQ.Core.SocketBase.ProcessCommands(Int32 timeout, Boolean throttle) at NetMQ.Core.SocketBase.GetSocketOption(ZmqSocketOption option) at NetMQ.Core.Utils.Selector.Select(SelectItem[] items, Int32 itemsCount, Int64 timeout) at NetMQ.NetMQPoller.Run() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

Would it be possible to fix this?

Thanks, Martin

somdoron commented 7 years ago

In the example, you are accessing the server socket from multiple threads, which is what causing the exception. You call dispose on the server from one thread but the server socket is also being used in the poller thread. First remove the server socket from the poller and then call dispose.

msykora commented 7 years ago

:( I tried to get some simple replication example and didn't get it right... The fact is, in real case my client is connecting to a remote ZMQ socket(JeroMQ implementation) and they close their socket it seems while my socket is trying to receive a message, and my whole application crashes on the aforementioned exception... BTW the same happens if firewall suddenly block outgoing connections, an exception from a background thread... And in this real use case I am running dispose etc. from the poller thread and handlign received messages the same way through receiveready... I can't easily provide simple code to replicate that, but it really is happening... can you help?