Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Genesis UDP Server and Client

, 21 Dec 2005
An article that shows the implementation of a lightweight UDP server and client with optional reliable channel.
genesis.zip
Genesis
Bin
GenesisChatClient.exe
GenesisChatServer.exe
GenesisCore.dll
Core
GenesisCore.csproj.user
GenesisChat.ico
GenesisChatClient
App.ico
GenesisChatClient.csproj.user
GenesisChatServer
App.ico
GenesisChatServer.csproj.user
/*
 * Genesis Socket Server and Client
 * (C)Copyright 2005/2006 Robert Harwood <robharwood@runbox.com>
 * 
 * Please see included license.txt file for information on redistribution and usage.
 */
#region Using directives

using System;
using System.Text;
using System.Collections;

#endregion

namespace GenesisCore
{
    /// <summary>
    /// Represents a single command
    /// </summary>
    internal class Command : ICommand
    {
		private string m_OPCode;
		private uint m_SequenceNum;
		private byte m_Flags;
		private short m_NumFields;
		private short[] m_FieldSizes;
		private string[] m_Fields;
		private string m_AllFields;

		/// <summary>
		/// Two byte OPCode that controlls how the command is used by the application
		/// </summary>
		public string OPCode
		{
			get{ return m_OPCode; }
			set{ m_OPCode = value; }
		}

		/// <summary>
		/// Sequence number of the command packet.
		/// Used as an identifier for sequencing and reliability.
		/// </summary>
        public uint SequenceNum
		{
			get{ return m_SequenceNum; }
			set{ m_SequenceNum = value; }
		}

		/// <summary>
		/// Command packet flags that control how the packet is processed.
		/// Such as making it reliable or sequenced (See the FLAGS_xxx constants.)
		/// </summary>
        public byte Flags
		{
			get{ return m_Flags; }
			set{ m_Flags = value; }
		}

		/// <summary>
		/// Number of data fields in this command
		/// </summary>
        public short NumFields
		{
			get{ return m_NumFields; }
			set{ m_NumFields = value; }
		}

		/// <summary>
		/// The sizes of the fields in the command
		/// </summary>
        public short[] FieldSizes
		{
			get{ return m_FieldSizes; }
			set{ m_FieldSizes = value; }
		}

		/// <summary>
		/// The data fields of the command, broken up and placed in a string array for convinience.
		/// </summary>
        public string[] Fields
		{
			get{ return m_Fields; }
			set{ m_Fields = value; }
		}

		/// <summary>
		/// A string containing all of the data fields concatonated together.
		/// </summary>
		public string AllFields
		{
			get{ return m_AllFields; }
			set{ m_AllFields = value; }
		}

        /// <summary>
        /// This function will populate the Fields property of a command object
        /// based on the FieldSizes and AllFields properties.
        /// </summary>
        public int Initialize()
        {
            if (NumFields == 0)
                return GenesisConsts.UDP_OK;

            try
            {
                int curpos = 0;
                Fields = new string[NumFields];
                for (int i = 0; i < NumFields; i++)
                {
                    Fields[i] = AllFields.Substring(curpos, FieldSizes[i]);
                    curpos += FieldSizes[i];
                }
                return GenesisConsts.UDP_OK;
            }
            catch
            {
                Fields = null;
                return GenesisConsts.UDP_FAIL;
            }
        }
    }

    /// <summary>
    /// A single entry in the reliable packet queue
    /// </summary>
    internal class ReliableEntry
    {
		/// <summary>
		/// Sequence number of the reliable packet
		/// </summary>
        public uint SequenceNum;

		/// <summary>
		/// Complete packet data including all header information, used if the system
		/// needs to resend a reliable packet.
		/// </summary>
        public string CommandPacket;
    }

    /// <summary>
    /// Implementation of a reliable packet queue
    /// </summary>
    internal class ReliableQueue : Queue
    {
		/// <summary>
		/// Initialises the reliable packet queue
		/// </summary>
        public ReliableQueue()
        {
            this.Clear();
        }

        /// <summary>
        /// Adds a command packet to the reliable queue.
        /// </summary>
        /// <param name="cmd">Command packet to add to the queue</param>
        /// <returns>UDP_OK or error code</returns>
        public int AddReliableCommand(ReliableEntry cmd)
        {
			if(this.Count >= GenesisConsts.MAX_RELIABLE_QUEUED)
				return GenesisConsts.UDP_RELIABLEQUEUEFULL;

            lock (this.SyncRoot)
            {
                if (this.Count > 0)
                {
                    ReliableEntry tmp = this.Peek() as ReliableEntry;
                    if (tmp != null)
                    {
                        if (tmp.SequenceNum == cmd.SequenceNum)
                            return GenesisConsts.UDP_ALREADYINQUEUE;
                    }
                }

                this.Enqueue(cmd);
            }
            return GenesisConsts.UDP_OK;
        }

        /// <summary>
        /// Ges the current reliable command from the queue, but does not remove it from the queue.
        /// </summary>
        /// <param name="cmd_out">Command at start of queue</param>
        /// <returns>UDP_OK or error code</returns>
        public int GetCurrentReliableCommand(out ReliableEntry cmd_out)
        {
            cmd_out = null;
            
            lock (this.SyncRoot)
            {
                //No reliable packets on the queue
                if (this.Count == 0)
                    return GenesisConsts.UDP_NOTFOUND;

                cmd_out = this.Peek() as ReliableEntry;
            }

            return GenesisConsts.UDP_OK;
        }

        /// <summary>
        /// Removes the command first in the queue, moving the queue along by one place.
        /// </summary>
        /// <returns>UDP_OK or error code</returns>
        public int NextReliableCommand()
        {
            lock (this.SyncRoot)
            {
                //No reliable packets on the queue
                if (this.Count == 0)
                    return GenesisConsts.UDP_NOTFOUND;

                this.Dequeue();
            }

            return GenesisConsts.UDP_OK;
        }

        /// <summary>
        /// Clears the reliable queue of all command packets.
        /// </summary>
        /// <returns>UDP_OK or error code</returns>
        public int ClearReliableQueue()
        {
            lock (this.SyncRoot)
            {
                this.Clear();
            }
            return GenesisConsts.UDP_OK;
        }

        /// <summary>
        /// Returns true if a command is waiting to be processed
        /// </summary>
        public bool CommandsWaiting()
        {
            lock (this.SyncRoot)
            {
                if (this.Count > 0)
                    return true;
                else
                    return false;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Rob Harwood
Web Developer
United Kingdom United Kingdom
Born in England, I have been programming since a very early age when my dad gave me prewritten programs to type in and run on a Sinclair ZX81 machine (seeing my name printed out on a TV screen was enough to keep me entertained!). I later did work using basic and STOS basic on the Atari ST and after that got my first PC and used Microsoft's QBasic. Later when I was about 13 I was in an airport and saw a trial copy of Visual Basic on a magazine, which I bought and it got me hooked on the Microsoft development tools.
 
Currently I am studying a software engineering degree and have been working with .NET since 1.0. I have just moved over to Visual Studio 2005/.NET 2.0 and am loving it! During my degree I have worked for a year at DuPont, where I ended up changing a lot of their old existing software over to .NET and improving it in the process! Since then I have been back and done some consulting work involving maintaining some of their older C++/MFC software.
 
While most of my current interestes involve .NET I am also confident in working with C++ in Win32, VB, Java, and have even done some development work on the Linux platform (although most of this involved ensuring that software I wrote in C++ was platform independent).
 
I have a strong passion for software technology, both higher level and more recently, systems level stuff (the dissertation I am doing for my degree is to implement a small compiler and virtual machine in C# for a Pascal-style language).

| Advertise | Privacy | Mobile
Web04 | 2.8.140821.2 | Last Updated 21 Dec 2005
Article Copyright 2005 by Rob Harwood
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid