UDP Pong - Server

Before jumping in, I want to note that none of the game logic lives in PongServer.cs.  That code is in Arena.cs.  Likewise, if you see a class called Arena, just know that the code for it will be on the next page.



This is another data structure but this one only lives on the Server.  It contains information about a connected Client and their current state.



Those three fields that have to deal with time might look a little confusing, let me explain them a bit better:

  • LastPacketReceivedTime - This is to denote the time that we received a Packet from a Client.  It is taken using a call to DateTime.Now, in the function PongServer._networkRun().  It's used to check for timeouts.
  • LastPacketSentTime - This marks the last time that we've sent a Packet to a Client.  It's recorded in the Arena._sendTo() method.  It's used to make sure that we don't spam a Client with data too often.
  • LastPacketReceivedTimestamp - Unlike the other two, this one is measured from Client time and not Server time.  It's taken from the Packet.Timestamp field.  When setting it, it should only be set if the value is higher than it was before.  It's used to make sure we only use the latest information from a Client.  Because in UDP some later sent datagrams might arrive before earlier sent ones, this will discard those old ones.



The duties of PongServer are only to read in data from the network, write it out to Clients, and manage the Arenas.



Alright, lemme explain what's going on here:

At the top of the class, we have quite a few ConcurrentQueues and ConcurrentDictionaries.  The first three are used for message processing.  Just to note, _activeArenas is being used as HashSet seeing as that collection doesn't have a concurrent counterpart.

The constructor's only job is to set the Port number and initialize the underlying UdpClient. It's set to listen in for all incoming IPv4 connections.

Start() is used to mark that our Server can start handling Clients.  Shutdown() is the opposite.  If the Server was already running, it will tell any active Arenas to stop their games.  Close() will clean up our resources.

_addNewAreana() is a small helper function that will startup a new Arena instance (also starts another Thread) and place it in the _nextArena variable.

NotifyDone() is only called by an Arena.  It tells the PongServer to remove any of its connected Clients from the _playerToArenaMap and itself from the _activeArenas dictionary.

Run() is the main method of the PongServer.  It will only execute it's code if Start() has been previously called.  At first, it will spin up a new Thread that will handle all of our incoming and outgoing messages (over the network) and instantiate the first Arena.  In it's while(running) loop, it will check if we have received a new NetworkMessage.

  • If the Packet is a RequestJoin, then it will add it to the next active Arena.  In the case that the _nextArena is full (TryAdd() returns false), it will then call _addArena() and try to add it again.
  • But if the Packet is not a RequestJoin, it will try to dispatch it to which Arena the Sender is in.
    • If you're wondering what happens if the Sender is not in any active Arena, the message will be discarded.

At the end of that loop Thread.Sleep() is called to save on CPU resources and a check is made to see if the server is still running or not.

_networkRun() is another important looping function.  It is run in it's own thread (the _networkThread).  While we haven't told the PongServer to stop, it will check to see if there are any datagrams waiting for us and write out any Packets that we've queued (both from _outgoingMessages and _sendByPacketTo).  In the event there wasn't anything to send or receive, we have the function take a tiny nap.  When were done listening for messages (i.e. the PongServer was told to shutdown), it will get all active Arenas to stop their Threads.  After that if there still are any connected Clients it will send a ByePacket to them.

SendPacket() and SendBye() are nearly identical in functionality; queuing up messages to the Clients.  The only exception is that the later function is used to disconnect Clients.

At the bottom of this file we have some code that is for program execution.  In the Main() function we create the PongServer instance and add an interrupt handler for when the user presses Ctrl-C in their consoles.

© 16BPP.net – Made using & love.
Back to Top of Page
This site uses cookies.