Stateless Frustrations Frustratingly Stated (Handling Improperly Disconnected UDP Clients?)

posted in Septopus for project Unsettled World
Published October 11, 2018
C#
Advertisement

So, after weeks of strange and seemingly intermittent/coincidental UDP server crashes(Receive Thread Halt/Un-catchable Exception) and the ensuing frustration that that causes when you aren't even trying to work on the server itself.  I sat down and spent a day trying to figure out the problem..  After an almost complete server rewrite from scratch with no real improvement in the problem.. I turned to random poking about on the internets..

Hmmm...  It seems like the problem only happens when I'm restarting the client..

Here's a post!!

https://stackoverflow.com/questions/38191968/c-sharp-udp-an-existing-connection-was-forcibly-closed-by-the-remote-host

And Here!

https://social.msdn.microsoft.com/Forums/en-US/6b7aa353-259b-4637-b0ae-d4e550c13e38/c-udp-socket-exception-on-bad-disconnect?forum=netfxnetcom

Yup, that's the problem for sure.

It appears that even though UDP isn't supposed to care about the other ends state, apparently this isn't true if the state is Port Unreachable.  When the server attempts to receive data (BeginReceiveFrom/BeginReceive/EndRecieveFrom/EndReceive/etc..) from an async connection and the client has (Crashed/Quit/Etc.) an ICMP port unavailable packet is generated and this causes the UDP socket to generate an exception that (even if you try/catch the hell out of it) causes the async receive thread to halt(I assume, this is the effect anyhow).

The best solution I've found, as stated in the link(s) above is to change the underlying IOControl behavior(Prevent the Exception from being thrown).


//This value tells the underlying IO system to IGNORE the ICMP error.
public const int SIO_UDP_CONNRESET = -1744830452;
private Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

_socket.IOControl((IOControlCode)SIO_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null);
_socket.Bind(new IPEndPoint(IPAddress.Any, port));

The first line is the value that needs to be set, and the second to last line is how you can set it.  And these are the live bits of code that are WORKING without error in my server, Finally! 

So, there, I've posted it again because it seems way more obscure than it should be.

Enjoy. ;)

0 likes 0 comments

Comments

Septopus

I guess it's worth adding that my server handles client timeouts as part of it's core behavior, simple timeouts for inactivity/etc, and doesn't use any of the UDP connections "state" info for the decision process..  So the only potential negative side-effect of not properly trapping this exception and removing the user's EndPoint immediately is that a few extra packet sends get attempted to the dead client before the player's EndPoint gets forgotten anyhow.  So far, it's not an issue, hopefully it stays that way. ;)

October 11, 2018 05:50 AM
Septopus

Additionally I may be experimenting with rewriting my server so that it doesn't use the VERY same thread for every single receive operation, doing this probably would have prevented my server from having issues in the first place as only the thread serving the dead client would be halting.  And the extra exception was already going unnoticed anyways. lol... 

October 11, 2018 06:02 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement