# Waiting on messages

## Recommended Posts

I'm trying to think of a nice way to handle this, but I can't, really. Let's say I'm using UDP*, so I have no guarantee packets are getting where I send them. So the client sends a message to the server asking for a piece of information (like what map the server is currently playing, etc). The client should wait 200 ms or so and if it doesn't get a response it sends the request again. But the client can't just sit there and wait for a response, it has to keep working, so the code looks something like this:
RequestMap()
{
SendRequest("what map is playing?");
}

Process() // called once per frame
{
if (needMap)
{
map = GotMapYet();
if (map)
needMap = false; // ok, we can continue on
else
{
if (timeSinceLastRequest > 200) // in milliseconds
{
RequestMap();
timeSinceLastRequest = 0;
}
}
}
}

What this bit of code means is that I'm going to have to have a finite state machine where I check for every possible state and handle it explicitly. That just seems really ugly, and that Process() function could quickly get huge. I'm sure this is a really common issue, so are there any good ways to approach it? Is there a name for this kind of problem? * Suggesting using TCP instead of UDP doesn't really have anything to do with this, since I'd still have cases where I have to wait on either the client or the server for a specific response even with TCP. This is just an example.

##### Share on other sites
That's a bad way to do things.
The client shouldn't request a map from the server, the server should send it when needed (log in and entering another map). The client should just wait until it gets the map, and then display it.
Similarly for getting other stuff, such as what the players nearby do (do they move, do they log out, fight, etc.)
And you should use TCP, unless you REALLY need UDP (for FPS maybe).
From your qustion it seems you are not very familiar with client/server communication in a MMO (or even multiplayer game) so using UDP will just make things worse.
Just use TCP until you get more experience, and then, if you really need to, switch to UDP.
I have years of MMO programming experience, and I still woldn't dare to switch to UDP. It can be a pain.

P.S. the process() function should do anything but dispatch all the incoming packets to the functions that handle them, based on the packet type. this way, your process() function won't become too big, and you can also organize your code better.

##### Share on other sites
hplus0603    11356
In general, you don't want to code you communications protocol state machine inside the actual application code. Instead, you want to define the protocol in a layer of your own, and re-use that layer for each transaction that goes across that protocol.

In the example you're suggesting (which might flood the server under certain conditions, as your re-transmit interval isn't adaptive), you'd probably have a layer that supports asynchronous requests:

class Response {  public:    Response() { done_ = false; data_ = 0; size_ = 0; }    ~Response() { free( data_ ); }    bool done_;    void * data_;    size_t size_;    void * request_;    size_t requestSize_;    Time transmitTimer_;};int serial;std::map< int, Response * > requests;Response * MakeReqeust( int code, void * params, size_t size ) {  ++serial;  Response * r = new Response;  requests[serial] = r;  // make packet containing code, serial, and params/size data  // put request into r->requestData_ etc  r->transmitTimer_ = 0;  return r;}void ConnectionPoll() {  // for each element in requests  Time t = Now();  if( r->transmitTimer_ < t ) {    r->transmitTimer_ = t + RETRANSMIT_TIME;    sendPacket( r->requestData_, r->requestSize_ );  }}void ReceivePacket( ... ) {  // if packet is of type "response to request"  aSerial = extract_serial_from_packet();  aSize = extract_data_size_from_packet();  requests[aSerial]->data_ = malloc( aSize );  requests[aSerial]->size_ = aSize;  memcpy( requests[aSerial]->data_, extract_data_from_packet(), aSize );  requests[aSerial]->done_ = true;  requests.erase( requests.find( aSerial ) );}void YourFunction() {  if( !map ) {    if( !mapRequest ) {      mapRequest = MakeRequest( GET_MAP, 0, 0 );    }    if( mapRequest->done_ ) {      map = makeMapFromData( mapRequest->data_, mapRequest->size_ );      delete mapRequest;      mapRequest = 0;    }  }}

Your main loop would call ConnectionPoll() to service outgoing requests, and ReceivePacket() to decode incoming packets.

You can make this much nicer in many ways. For example: refining the protocol header (request codes, serial, etc) to support something "real" (timing, re-transmits, etc); supporting cancellation of requests; configuring requests with a callback that's called when the request completes; using templates and pointer-to-member-function to cut out typing; using co-routines to provide the illusion of synchronous request execution; etc.

##### Share on other sites
Icefox    238
I have no idea what hplus is talking about (sorry!), but I ran into a similar problem and solved it pretty simply just by making a thread whose job was to block and wait for incoming UDP packets, get the information, and stick it on a queue (or you might want to use a stack or something else). Then my actual application code just has to poll the queue every now and then to see if there's anything in it.

I later learned this was called a producer-consumer pattern. Fancy that.

##### Share on other sites
pDK    122

http://www.codeproject.com/internet/jbsocketserver1.asp

It should give you a good start as to understanding what HPlus means...

Keep in mind that this was originally created for a Server application, but considering your program is "message-pushed" (what is the correct term I'm looking for??? correct me please), this would work for you as well.

I would then continue through the whole set of articles in this series (seven) to get a full grasp as to what you want to do.

ONE thing that HPlus mentioned that is not included in this series (which I dunno why not) is the use of TTL (time to live) on the packets... I would incorporate that somewhere.

- Eric

##### Share on other sites
hplus0603    11356
Icefox:

The problem that OP had was not "how do I receive data in my program" but instead "how do I manage state when I have asynchronous operations, possibly lossy, outstanding, and may need to re-send data if it gets lost."

My suggestion was to encapsulate the behaviors of "having an asynchronous request outstanding" and "wanting to send a message to the server" into a single object that can be re-used everywhere you need this behavior. You can also factor these behaviors into two separate, smaller, behaviors, and compose them.