Issue in catching forcibly Closed Socket Exception.

6 views
Skip to first unread message

Fawad Zuberi

unread,
Mar 24, 2026, 7:51:08 AM (4 days ago) Mar 24
to netmq-dev
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.

Reply all
Reply to author
Forward
0 new messages