Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | Related Pages | Examples

Protocol

Introduction

Our framework is built on top of the UDP protocol. By doing this we are able to make use of this protocol's error detection service. But in order to offer the services we wish to we need to build some additional services on top of the UDP protocol. We will in this part of the report give a description of these services.

Implications of the transport layer used

As described in the report we have chosen to build our framework on top of the UDP transport layer protocol. This choice has the following implications on the building of our framework.

Services offered

UDP offers no guarantees other than error detection but does have a low header overhead and allows us to build on top of it with almost no restrictions except those described in section Limitations.

Limitations

We have no flow control, congestion control, reliability or error correction. Furthermore the limit on a UDP datagram's size is 65507, or 64K-8-20. 8 for the UDP packet header and 20 for the IP-headers. This puts a restriction on the size of data sent with this protocol in one packet to 65507 bytes.

Packet formats

Framework packets consists of the five fields shown in figure The-header-format.

Figure 1: The header format of framework packets.
img1.png

The main functions and formats of the fields are as follows:

Data formats

All data received by the framework will be expected being in network byte order and the framework will make sure to convert it into host byte order before delivering it. The fundamental data types are those used in the C++ programming language which are described in the C++ reference manual1. All data sent from the framework are also sent in network byte order.

The maximum size of a framework packet is limited to 65507 (header included) due to the use of UDP as the underlying transport layer.

Connection

Although the GaNeF protocol is built on top of the UDP protocol it is not connectionless. Connection between client and server is established, maintained and closed down. This section will go through the various phases of the client/server connection.

Handshaking

To establish connection between a client and a game server the handshaking protocol shown on figure The-handshaking-phase is used.

Figure 2: The handshaking phase.
img2.png

The client initiates the handshaking with a request, which is the packet called INIT PACKET on figure The-handshaking-phase. It is possible for the game developer to send some data along with this initial request - this data is what we call tagalong on the diagram and the framework will ask the game for this data. Such data could for instance be the client's user name and password which would then be delivered to the game at the server side of the framework along with the initial packet. The server can reply to the INIT PACKET in two ways. The first is by telling the client that it is not possible for it to connect to this server at this time; this message is the first DISCONTINUE

message shown on the figure (again it is possible for the game developer to send some data along with this message). If the server accepts the client it will reply the INIT PACKET with a SETUP DESCRIPTION packet containing a description of the server's setup. The client can reply to this message in one of two ways. If it cannot conform to the description given by the server it will reply with a DISCONTINUE message. If it can conform to the setup described by the server it will reply the SETUP DESCRIPTION message with a READY message letting the server know that the client is now ready to receive packets from the server.

States

On figure The-handshaking-phase we see a description of the states the client enters as the handshaking phase evolves. These are states used and maintained inside the framework Mainly for the server and client to know in which way to handle the packets on their incoming socket.

Heartbeats

After the connection has been established the connection needs to be maintained. This is done by the client sending heartbeats to the server. As mentioned in section Handshaking the server will disconnect the client if it has not heard anything from it during the period of time stated in the SETUP DESCRIPTION message. In this implementation the client sends heartbeats to the server after half of that period of time has passed. The syntax of a heart beat packet is Reliable = 1, Type = 0, Priority = 0, Receiver = 30001, Sequence Number = any sequence number will do.

Close down of connection

When a client wishes to disconnect it can just stop sending packets to the server. When this happens the server will timeout the client and disconnect then it. This also means that if the game developer wishes to send some data from the client to the server on disconnect, he has to do so explicitly; that is, he cannot do it by sending a message along with any disconnect message sent between the frameworks as he was able to during the connection establishment.

Guarantees

packets

Our framework makes no guarantee on delivery when packets are sent unreliably. It works by best effort and sends unreliable packets by UDP. However, it does guarantee that the unreliable packets which arrives in the other end are delivered in the order they were sent. Packets arriving out of order on the receiving side are simply discarded. Please see figure The-reception-of. Sending packets unreliably guarantees that the packet will be delivered at most once. To send a packet unreliably the Reliable field of the packet header should be set to 0.

Figure 3: The reception of unreliable packets at the receiver. The number indicates a packet's sequence number. Packets marked with an arrow will be discarded on the receiving side since they arrive out of order.
img3.png

Reliable packets

Our framework can guarantee delivery of packets if they are sent reliably. When packets are sent reliable It also guarantees that the packets are delivered in the same order as they were sent. Sending a packet reliable also means that the packet is guaranteed to be delivered exactly once at the receiving side.

Figure 4: The buffering of reliable packets. Reliable packets are buffered until they are ready for delivery to ensure they are delivered in the right order.
img4.png

This is done by storing packets temporarily in a buffer until all preceding packets have been received. When all these packets have been received the packets waiting are marked as being ready for delivery and thereby delivered when the main loop of the game is ready for this. To send a packet reliably the packet header field Reliable should be set to 1.

Acknowledgements

Whenever the framework receives a reliable packet it lets the sender know that is has received this packet by sending by an acknowledgement. An acknowledgement packet has the format shown on figure Format-of-an. In our implementation acknowledgement packets are sent unreliable. This can be done so because if the acknowledgement packet is lost during transfer the sender of the original reliable packet will notice this and resend the packet. In this case we can re-acknowledge the packet. Also, if we sent the acknowledgement packet reliable we would enter a never-ending loop where acknowledgement packets are acknowledged with acknowledgements which again are acknowledge with acknowledgements and so forth.

Figure 5: Format of an acknowledgement packet. Only the fields filled out will be used - the rest of the fields are ignored by the framework.

img5.png

Timeouts

A possibly lost packet is discovered by the framework by timeouts. All packets sent reliable are given a timeout. The timeout is calculated as the average round trip time (RTT) multiplied by a factor chosen by the game developer. A RTT is the time passed from when a reliable packet has been sent until we have received its acknowledgement. The calculation of the average timeout is done with a precision 2 decided by the game developer. When a timeout occurs the packet in question is resent by the framework. Please note that no negative acknowledgements are sent back to the game.

Queues

At both the sending and the receiving side we maintain queues. At the sending side for packets waiting to be sent and on the receiving side for packets waiting to be delivered. These queues are implemented in such a way that hey have influence on in which order packets are sent or delivered. Firstly packets which the lowest priority are sent/delivered first. Then we we look at the sequence number of the packet, then the type (this means that e.g. system packets are sent/delivered before other types of packets, see section Packet formats), then we look at the ID of the receiving object (receiver on figure The-header-format) and finally we look at how this packet is to be sent to (client ID) this last check is only relevant on the server since this last values always will be the same on the clients, namely 0. This scheme means that we cannot allow packets with different priority to use the same sequencer since this would cause the packets to be delivered out of order (because high priority packets are delivered first. Therefore we maintain a channel for each priority so that each priority is given its own sequencer. Furthermore, on the server side each client is also given its own sequencer.

We also allow for unreliable packets to be replaced by newer packets in the queue. This will happen if an unreliable packet arrives at the queue with the same priority, type, receiver and client ID as an existing. If this is the case, we assume that this packet is a newer update of the same type to the client in question - this is valid for both in- and out-queues. The sequence number is irrelevant in this, since unreliable packets are given a sequence number the moment they are sent, and therefore all have the same sequence number when in the out-queue.

Sequence numbering

Unreliable packets

All unreliable packets sent by the framework with the same priority share the same sequencer. For every unreliable packet sent the unreliable sequence number is increased by one. For the server this means that if it sends an unreliable packet with sequence number J to client X with priority U, the next unreliable packet it will send will have sequence number J+1 - if this packet is also sent to client Y and has priority U. This conforms with the description given in section Unreliable packets where we guaranteed at-most-once delivery for unreliable packets sent with the same priority.

Reliable packets

The scheme for reliable packets is a little more complex. The server assigns sequencers to the clients in the following way. For each priority a packet can have it receives on sequencer. This means that each client are assigned 256 sequencers for priority 0 through 255. So at any given time each of the priorities may have a different sequence number as shown on figure sequenceno. The clients use the same sequencing scheme, however the clients only have one ``client'' which is the server.

Figure 6: Reliable packets are given a sequence number depending on the priority they have and the client they are destined for.
img6.png

Sequence number wrap

As the framework is implemented wraps are not handled. Wraps occur when sequence numbers become too large to fit within the data type they are represented with. For sequence numbers in the framework, which are represented by unsigned integers, their maximum value is 4,294,967,295. This means that the framework can send 1000 packets per second before the sequence number wraps after 49.7 days given that the same priority and reliability is used for all packets - in our framework this would lead to a in communication since lower sequence numbers are not accepted. If we assume that all priorities are used equally, the number of days is 12725.8 before the first sequence number wraps.


Footnotes

1 http://www.cplusplus.com/doc/tutorial/variables.html

2 Precision here meaning the number RTTs to calculate the average on.


Generated on Mon Feb 6 12:24:58 2006 for Ganef by  doxygen 1.4.4