Skip to content

Issue in catching forcibly Closed Socket Exception. #1143

@FawadZuberi

Description

@FawadZuberi

Bug Report: Unhandled SocketException on Proactor I/O Thread Causes Process Termination
Package
LogicielNetMQ v4.0.0 (forked from NetMQ)
Environment
• .NET 8 / .NET 10 (client app), .NET Standard 2.0 (communication library)
• Windows, x64
• Sockets used: subscriberSocket, dealerSocket with NetMQPoller
• ZMQ heartbeat enabled (HeartBeatInterval + HeartbeatTimeout + TcpKeepalive)

Summary
When all network packets to the remote server are dropped (simulated using Clumsy network blocker tool), the application process terminates with an unhandled System.Net.Sockets.SocketException thrown on NetMQ's internal Proactor I/O thread. Because the Proactor runs on a raw System.Threading.Thread, the exception cannot be caught or suppressed by application code — in .NET Core/.NET 5+, AppDomain.CurrentDomain.UnhandledException is notification-only and cannot prevent process termination.

Full Stack Trace
System.Net.Sockets.SocketException
HResult=0x80004005
Message=An existing connection was forcibly closed by the remote host.

at NetMQ.Core.Mailbox.Send(Command cmd) -- Mailbox.cs:line 36
at NetMQ.Core.SocketBase.TrySend(Msg& msg, TimeSpan timeout, Boolean more) -- SocketBase.cs:line 641
at NetMQ.Core.MonitorEvent.Write(SocketBase s) -- MonitorEvent.cs:line 141
at NetMQ.Core.SocketBase.EventDisconnected(String addr, AsyncSocket ch) -- SocketBase.cs:line 1075
at NetMQ.Core.Transports.StreamEngine.Error() -- StreamEngine.cs:line 268
at NetMQ.Core.Transports.StreamEngine.ProcessInput() -- StreamEngine.cs:line 782
at NetMQ.Core.Transports.StreamEngine.FeedAction(Action action, SocketError socketError, Int32 bytesTransferred) -- StreamEngine.cs:line 283
at NetMQ.Core.Utils.Proactor.Loop() -- Proactor.cs:line 129
at System.Threading.Thread.StartHelper.Callback(Object state)

Root Cause Analysis

System.Net.Sockets.SocketException
HResult=0x80004005
Message=An existing connection was forcibly closed by the remote host.

at NetMQ.Core.Mailbox.Send(Command cmd) -- Mailbox.cs:line 36
at NetMQ.Core.SocketBase.TrySend(Msg& msg, TimeSpan timeout, Boolean more) -- SocketBase.cs:line 641
at NetMQ.Core.MonitorEvent.Write(SocketBase s) -- MonitorEvent.cs:line 141
at NetMQ.Core.SocketBase.EventDisconnected(String addr, AsyncSocket ch) -- SocketBase.cs:line 1075
at NetMQ.Core.Transports.StreamEngine.Error() -- StreamEngine.cs:line 268
at NetMQ.Core.Transports.StreamEngine.ProcessInput() -- StreamEngine.cs:line 782
at NetMQ.Core.Transports.StreamEngine.FeedAction(Action action, SocketError socketError, Int32 bytesTransferred) -- StreamEngine.cs:line 283
at NetMQ.Core.Utils.Proactor.Loop() -- Proactor.cs:line 129
at System.Threading.Thread.StartHelper.Callback(Object state)

Step-by-step:

  1. Proactor.Loop() receives an I/O completion with SocketError != Success (remote connection dropped).
  2. StreamEngine.FeedAction() detects the error and calls StreamEngine.Error().
  3. Error() calls SocketBase.EventDisconnected() on the monitored socket.
  4. EventDisconnected() checks (m_monitorEvents & SocketEvents.Disconnected) != 0 — if a NetMQMonitor was attached, this is true.
  5. It calls MonitorEvent.Write(m_monitorSocket) which does m_monitorSocket.TrySend(ref msg, ...).
  6. Inside TrySend, the internal Mailbox.Send(Command cmd) writes a command and calls m_signaler.Send().
  7. The Signaler uses a pair of localhost TCP sockets (127.0.0.1) for inter-thread notification. Under heavy network disruption (e.g., Clumsy blocking all packets), this Socket.Send() call also throws SocketException.
  8. This exception is unhandled on the Proactor thread — Proactor.Loop() has no try/catch for SocketException around the completion handler invocation.
  9. In .NET Core, an unhandled exception on a raw Thread terminates the process. AppDomain.UnhandledException fires but cannot prevent termination.

Please help me catch this Exception or handle it gracefully thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions