This project is read-only.

Missing timeout handling

Feb 28, 2015 at 2:33 AM
I just started using this library and noticed that MasterServer.GetAddresses() doesn't have any timeout handling built in.
Due to the async/callback nature of this method the caller won't block, but it will never receive a callback and unanswered UDP requests will accumulate in the background.
Feb 28, 2015 at 10:48 AM
Edited Feb 28, 2015 at 10:49 AM
hi hbeham,

MasterServer.GetAddresses() uses BeginReceive and EndReceive to receive IPs.
It doesn't come with a timeout because BeginReceive waits till it could read any data in internal buffer.i.e. it waits indefinitely
As soon as the internal buffer gets filled(partial or full) with data,it reads that data and invokes the callback.
and within the callback ,I call the BeginReceive again and thus forms a loop and no data is left unread(or unanswered).
It exits out only when the seedpoint ip is received.

If you want to stop it from receiving any more ips then you can call Dispose() method
Feb 28, 2015 at 1:13 PM
Hi betson, thanks for your reply and sharing your code with us!

My applolgies, I was under the wrong impression that with BeginReceive() background threads pile up when no response is received, but that is not true as I found out with the debugger.

What I noticed is that GetAdresses() sets the internal IsListening flag and never resets it. Thus a single MasterServer instance can only be used once and silently does nothing when used again.
So the question is when to properly dispose the instance. I can't do it right after calling GetAddresses, which would interrupt the pending request, nor can I do it in the receive callback, because it might not be called for the final batch if a UDP packet got dropped.
If it's not disposed at all, open UDP ports pile up, which can be seen with "netstat -an".

The current implementation of GetAddresses() works fine as a low-level API, but leaves some important tasks up to the caller:
  • resending unanswered UDP packets
  • failure handling, like partial results or no results at all
  • resource cleanup
I'm working on a wrapper now that takes care of these issues and will share it when completed.
Mar 1, 2015 at 1:18 AM
Edited Mar 1, 2015 at 1:54 AM
I experimented with all kinds of async patterns today and came to the conclusion that async is not a good fit for this request/response scenario.
The amount of overhead needed to properly synchronize the various threads and protect against race conditions was just enormous.
The simplest solution was to do make the communication synchronous and wrap the call into a Task which runs in the thread pool.

This implementation tries Send/Receive up to 3 times per batch to recover from dropped packets, which now allows me to retrieve all 6000 Team Fortress 2 servers.
In case it really fails 3x on the same batch, the callback is notified by passing it a NULL value.
The caller can use the returned Task object to Wait() for completion or rethrow a exception if necessary.