An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
Thanks for sharing your code. Looking at the implementation, the issue stems from a combination of thread blocking and resource lifecycle management rather than a localhost restriction. This is why a manual delay altered the behavior—it artificially shifted the application's timing to hide a race condition.
There are two main architectural issues here:
- The Single-Handoff Limitation: The legacy BeginAcceptTcpClient method only registers a listener for a single connection attempt. Once a client connects, the listener stops accepting new traffic unless you manually queue up another BeginAccept call from within your connection callback.
- Thread Blocking: Relying on _stopEvent.Wait() completely halts your setup thread. If the background thread callback interacts with shared state or encounters an unhandled exception before signaling that event, it will result in a deadlocked state or a silent application crash.
Recommended Solution
The Begin/End Asynchronous Programming Model (APM) used here is legacy and highly susceptible to timing bugs. It is much safer to implement the modern Task-based Asynchronous Pattern (TAP) using async/await. This handles the state machine and exceptions inherently, allowing you to use a cancellation token loop instead of manual thread blocking primitives.
Rather than rewriting the legacy callback logic, you can review Microsoft's official production-ready guidance and complete code samples for managing asynchronous TCP listeners here:
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/sockets/tcp-classes