Skip to content

[Windows] Server UDP receive path permanently stops after a remote client process is force-killed (WSAECONNRESET; surviving clients dropped, no new connections) #4096

Description

@dbsvkdrn

Description

On Windows, when a remote client process is force-killed (Task Manager kill / crash — no Disconnect packet sent), the server's UDP receive path stops permanently:

  • All other still-running clients get disconnected shortly after (their reliable pipeline reports transport loss).
  • No new client can connect ever again (connect attempts time out with "Failed to connect to server").
  • The server process stays alive with zero managed exceptions and zero transport error logs — it fails silently.
  • The wedge is permanent: waiting well past the disconnect timeout (80+ s) does not recover it.

A graceful quit of the same client (NetworkManager.Shutdown() then Application.Quit()) does not reproduce the issue — subsequent clients connect fine. A fresh server also accepts multiple simultaneous clients fine, so this is specifically the ungraceful-disconnect path.

We consistently reproduce this with all processes on the same machine (127.0.0.1), and expect the same on same-LAN direct UDP. (Other network topologies not tested.)

Suspected root cause

This matches the classic Windows UDP WSAECONNRESET behavior: after the client dies, the server keeps sending to the dead port for a while (snapshots/heartbeats), Windows returns ICMP Port-Unreachable, and because the underlying socket does not have SIO_UDP_CONNRESET disabled, the shared server socket's receive path is affected.

From reading com.unity.transport sources shipped with the editor (6.5.0, Runtime/UDPNetworkInterface.cs):

  • We could not find any place where SIO_UDP_CONNRESET is disabled for the UDP socket created via Baselib_RegisteredNetwork_Socket_UDP_Create, and there is no NetworkSettings knob for it.
  • The socket re-creation safety net in the receive job only triggers when all receive requests fail AND getsockname() fails. In this wedge the socket handle stays valid (getsockname() succeeds), so recovery never runs — consistent with our logs showing neither "Socket error encountered; attempting recovery" nor "Unrecoverable socket failure".

Reproduction steps (same machine, Windows)

  1. Build a minimal NGO project (NetworkManager + UnityTransport, default settings, port 7777, a trivial player prefab).
  2. Start server/host process.
  3. Start client A and client B (both connect fine; server logs ClientConnected for both).
  4. Force-kill client A (taskkill /F /PID <A>).
  5. Observe: server logs ClientDisconnected for A after the disconnect timeout (~30 s), then client B is disconnected as well; any new client C fails to connect ("Failed to connect to server"). Server stays silent — no exceptions, no socket errors, and it never accepts a connection again.
  6. Repeat the run but close client A gracefully (Shutdown + quit) instead: client B stays connected and new clients connect normally.

Reproduces 100% for us across many runs (release and development builds, -batchmode -nographics and windowed).

Environment

  • Unity 6000.5.2f1 (Windows 11 host, all processes on one machine)
  • com.unity.netcode.gameobjects 2.13.0
  • com.unity.transport 6.5.0 (editor-bundled)
  • UnityTransport with default settings, direct UDP (no Relay), 127.0.0.1:7777

Expected outcome

A force-killed (crashed) client must not take down the server's transport. The server should drop that connection after the timeout and keep serving existing/new clients — which is exactly what happens on the graceful-disconnect path.

Workaround we use

Always terminate clients gracefully in development/smoke tests. This obviously cannot cover real crashes, which is why we're reporting it.

We can provide full server/client logs of both the failing (force-kill) and passing (graceful) runs on request.

Metadata

Metadata

Labels

stat:InvestigatingIssue is currently being investigatedstat:awaiting-responseAwaiting response from author. This label should be added manually.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions