Getting Online with Multiplayer Gaming(11)
The Message Queue
The server never deals directly with incoming messages; instead, the server pulls
messages from the queue. If a message needs to be processed, it must be inserted
into the queue. Using a queue ensures that the server never gets bogged down with
processing incoming network data.
The queue is just an array of sMsg structures that is allocated when the application
class is initialized. I set a limit of 1,024 messages to be allocated for the server, but you
can change that amount just by altering the MAX_MESSAGE macro in the source code.
To track messages being added and removed from the queue, use two variables—
m_msg_head and m_msg_tail. Check out Figure 19.12 to see how the queue uses those
two variables to track which messages are to be inserted or removed.
Whenever a message needs to be added to the message queue, a special function is
called. That function is cApp::queue_msg, and it takes a single argument: the
sMsg structure to add to the queue.
Remember the incoming message functions of cApp (covered in the section “DirectPlay
Messages to Game Messages”)? Those functions built a message structure and added
the message to the queue via queue_msg. Look at the queue_msg code to see what’s
going on:
{
const sMsgHeader* header = ( const sMsgHeader*) msg;
// return if no room left in queue
if ((m_msg_head + 1) % MAX_MESSAGES == m_msg_tail)
return false ;
// stuff message into queue
if (header->size <= sizeof (sMsg))
{
EnterCriticalSection(&m_msg_cs);
memcpy(&m_msgs[m_msg_head], msg, header->size);
// goto next empty message (flip around if at end)
m_msg_head++;
if (m_msg_head >= MAX_MESSAGES)
m_msg_head = 0;
LeaveCriticalSection(&m_msg_cs);
}
return true ;
}
As you can see, queue_msg merely copies the supplied sMsg structure into the
next available element in the message array (pointed to by m_msg_head). Two things
you haven’t seen are the EnterCriticalSection and LeaveCriticalSection functions.
Windows uses these two functions to restrict the application’s access to memory
(using the EnterCriticalSection function), only allowing a single process to modify
that memory. Once you finish modifying the memory, you need to inform
Windows by calling LeaveCriticalSection.
Although this may not make sense at first, think about it like this—the network
component (a process) is running in the background at the same time as the application
(another process). If the network component is adding messages to the
array while the application is trying to remove messages or modify the messages,
the program data can become corrupt. Critical sections ensure that only one
process gets sole access to data for a short time.