Click here to Skip to main content
15,892,965 members
Articles / Programming Languages / C#

Genesis UDP Server and Client

Rate me:
Please Sign up or sign in to vote.
4.97/5 (46 votes)
21 Dec 20059 min read 243K   8.2K   134  
An article that shows the implementation of a lightweight UDP server and client with optional reliable channel.
/*
 * 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.
 */
using System;
using System.Net;

namespace GenesisCore
{
	/// <summary>
	/// Represents the public interface for a connection.
	/// </summary>
	public interface IConnection
	{
		/// <summary>
		/// Gets the encryption key being used for the connection (emptry string means no encryption).
		/// </summary>
		string EncryptionKey
		{
			get;
		}

		/// <summary>
		/// On a connection created at the client side, gets the request id generated when the connection was
		/// first requested to the server.
		/// </summary>
		string RequestID
		{
			get;
		}

		/// <summary>
		/// Gets the remote end point representing the remote computer for this connection.
		/// </summary>
		IPEndPoint RemoteEP
		{
			get;
		}

		/// <summary>
		/// Gets the ID of the last sent unreliable packet.
		/// </summary>
		uint LastSentPacket
		{
			get;
		}

		/// <summary>
		/// Gets the ID of the last received unreliable packet.
		/// </summary>
		uint LastReceivedPacket
		{
			get;
		}

		/// <summary>
		/// Gets the ID of the last sent reliable packet.
		/// </summary>
		uint LastSentPacketR
		{
			get;
		}

		/// <summary>
		/// Gets the ID of the last received reliable packet.
		/// </summary>
		uint LastReceivedPacketR
		{
			get;
		}

		/// <summary>
		/// Gets the ID of the last received sequenced unreliable packet.
		/// </summary>
		uint LastReceivedPacketSeq
		{
			get;
		}

		/// <summary>
		/// Gets a value that shows whether or not this connection is authenticated.
		/// </summary>
		bool Authed
		{
			get;
		}

		/// <summary>
		/// Gets a value that determines whether the connection is a server or client connection.
		/// </summary>
		bool Server
		{
			get;
		}

		/// <summary>
		/// Gets a value that determines when the connection will be regarded as having "timed out".
		/// </summary>
		DateTime TimeoutTime
		{
			get;
		}

		/// <summary>
		/// Gets or sets an object that can be set to anything specific to the user application and connection.
		/// </summary>
		object UserObject
		{
			get;
			set;
		}

		/// <summary>
		/// Sends an unreliable command to the remote client.
		/// </summary>
		int SendUnreliableCommand(byte flags, string opcode, string[] fields);

		/// <summary>
		/// Sends a reliable command to the remote client.
		/// </summary>
		int SendReliableCommand(byte flags, string opcode, string[] fields);
	}

	/// <summary>
	/// Interface that represents a command sent over the network.
	/// </summary>
	public interface ICommand
	{
		/// <summary>
		/// Gets the 2 byte opcode for the command message.
		/// </summary>
		string OPCode
		{
			get;
		}

		/// <summary>
		/// Gets the unique ID of the command packet, the number is only unique for the connection it occured in.
		/// </summary>
		uint SequenceNum
		{
			get;
		}

		/// <summary>
		/// Gets various properties of the command (such as whether or not it is sequenced).
		/// </summary>
		byte Flags
		{
			get;
		}

		/// <summary>
		/// The number of data fields in the command.
		/// </summary>
		short NumFields
		{
			get;
		}

		/// <summary>
		/// The size of each data field within the command.
		/// </summary>
		short[] FieldSizes
		{
			get;
		}

		/// <summary>
		/// The actual data fields within the command.
		/// </summary>
		string[] Fields
		{
			get;
		}

		/// <summary>
		/// Gets a single string containing all of hte data fields concatenated.
		/// </summary>
		string AllFields
		{
			get;
		}
	}

	public interface IGenesisUDP
	{
		/// <summary>
		/// Fired when a debug message is generated by the UDP server (DEBUG builds only).
		/// </summary>
		event DebugHandler OnDebugMessage;

		/// <summary>
		/// Fired when some data is read from the UDP socket, allows access to the raw information.
		/// </summary>
		event ReceivedHandler OnDataReceived;

		/// <summary>
		/// Fired when the UDP server starts or stops listening.
		/// </summary>
		event ListenHandler OnListenStateChanged;

		/// <summary>
		/// Fired when the UDP socket generates an error.
		/// </summary>
		event SocketErrorHandler OnSocketError;

		/// <summary>
		/// Fired when a command arrives that has no connection related to it (such as connection requests).
		/// </summary>
		event IncomingCommandHandler OnConnectionlessCommand;

		/// <summary>
		/// Fired when a client connection sends its login data.
		/// </summary>
		event ConnectionAuthHandler OnConnectionAuth;

		/// <summary>
		/// Fired when a server has requested login information.
		/// </summary>
		event SendLoginHandler OnLoginRequested;

		/// <summary>
		/// Fired when a command has been recieved for an authorized connection.
		/// </summary>
		event IncomingCommandHandler OnCommandReceived;

		/// <summary>
		/// Fired when a connection to a remote host is established or lost.
		/// </summary>
		event ConnectionStateChangeHandler OnConnectionStateChanged;

		/// <summary>
		/// Fired when a server has accepted a client connection.
		/// </summary>
		event AuthenticatedHandler OnAuthFeedback;

		/// <summary>
		/// Fired when a connection request times out.
		/// </summary>
		event RequestTimedOutHandler OnConnectionRequestTimedOut;

		/// <summary>
		/// Gets the current state of the UDP server (idle, listening etc.)
		/// </summary>
		int State
		{
			get;
		}

		/// <summary>
		/// Gets or sets a value that determines whether or not new connections should be encrypted.
		/// </summary>
		bool Encrypt
		{
			get;
			set;
		}

		/// <summary>
		/// Starts listening on the given local IP address and port.
		/// </summary>
		/// <param name="IP">The local IP address to listen on (use an empty string to bind to all available).</param>
		/// <param name="Port">The port number to bind to.</param>
		/// <returns>UDP_OK on success, UDP_FAIL on failure.</returns>
		int StartListen(string IP, int Port);

		/// <summary>
		/// Stops the UDP server listening and drops all open connections.
		/// </summary>
		/// <returns>UDP_OK on success or UDP_FAIL if the UDP server is not listening.</returns>
		int StopListen();

		/// <summary>
		/// Sends a connectionless command to a remote host.
		/// </summary>
		/// <param name="IP">The IP address of the remote host.</param>
		/// <param name="Port">The remote port number.</param>
		/// <param name="opcode">The command opcode.</param>
		/// <param name="fields">The command data fields.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_RELIABLENODESTINATION when attempting to send a reliable packet.
		/// UDP_UNRELIABLETOOLONG if the packet data fields are too big.
		/// UDP_FAIL on general failure.
		/// </returns>
		int SendConnectionlessCommand(string IP, int Port, string opcode, string[] fields);

		/// <summary>
		/// Sends an unreliable command to a group of connections.
		/// </summary>
		/// <param name="filter">Filter flags to allow filtering in/out certain types of connections.</param>
		/// <param name="flags">The flags for the command.</param>
		/// <param name="opcode">The command opcode.</param>
		/// <param name="fields">The command data fields.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_RELIABLENODESTINATION when attempting to send a reliable packet.
		/// UDP_UNRELIABLETOOLONG if the packet data fields are too big.
		/// UDP_FAIL on general failure.
		/// </returns>
		int SendUnreliableCommandToAll(BroadcastFilter filter, byte flags, string opcode, string[] fields);

		/// <summary>
		/// Sends a reliable command to a group of connections.
		/// </summary>
		/// <param name="filter">Filter flags to allow filtering in/out certain types of connections.</param>
		/// <param name="flags">The flags for the command.</param>
		/// <param name="opcode">The command opcode.</param>
		/// <param name="fields">The command data fields.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_RELIABLEQUEUEFULL if the reliable queue is full.
		/// UDP_FAIL on general failure.
		/// </returns>
		int SendReliableCommandToAll(BroadcastFilter filter, byte flags, string opcode, string[] fields);

		/// <summary>
		/// Attempt to connect to a Genesis UDP server.
		/// </summary>
		/// <param name="RemoteIP">IP address or DNS hostname of the remote host. This string will be changed to an IP if a hostname is passed and resolved.</param>
		/// <param name="RemotePort">The port on the remote server.</param>
		/// <param name="request_id">This parameter is set to the generated request ID.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_UNABLETORESOLVE when a DNS hostname failed to resolve.
		/// UDP_ALREADYCONNECTED if a connection to the given host and port already exists.
		/// </returns>
		int RequestConnect(ref string RemoteIP, int RemotePort, out string request_id);

		/// <summary>
		/// Cancels a connection attempt to a remote host.
		/// </summary>
		/// <param name="RemoteIP">The IP address of the remote host that has a pending connection request.</param>
		/// <param name="RemotePort">The remote port.</param>
		/// <returns>UDP_OK</returns>
		int CancelConnect(string RemoteIP, int RemotePort);

		/// <summary>
		/// Cancels a connection attempt to a remote host.
		/// </summary>
		/// <param name="RequestID">The request ID of the connection attempt to be aborted.</param>
		/// <returns>UDP_OK</returns>
		int CancelConnect(string RequestID);

		/// <summary>
		/// Gets a reference to a connection given the remote hosts IP and port.
		/// </summary>
		/// <param name="RemoteIP">IP address of the remote host.</param>
		/// <param name="RemotePort">The port on the remote host.</param>
		/// <param name="conn">Set to the connection that is found, or set to null if not found.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_NOTFOUND when a matching connection is not found.
		/// </returns>
		int ConnectionByIPPort(string IP, int Port, out IConnection conn);

		/// <summary>
		/// Disconnects a connection to a remote host, optionally sendin a disconnect message and reason.
		/// </summary>
		/// <param name="conn">The connection interface to disconnect.</param>
		/// <param name="send_disconnect_packet">True if a disconnection packet should be sent to the remote host.</param>
		/// <param name="reason">If a disconnection packet is to be sent, this string will be sent as the reason for disconnection.</param>
		/// <returns>UDP_OK</returns>
		int RemoveConnection(IConnection conn, bool send_disconnect_packet, string reason);

		/// <summary>
		/// Returns an array of all connections of a specific type.
		/// </summary>
		/// <param name="servers">True if connection to servers should be returned, false to return conections to clients.</param>
		/// <param name="found">This parameter will be set to a reference of the array containing the found connections.</param>
		/// <returns>
		/// UDP_OK on success.
		/// UDP_NOTFOUND when a matching connection is not found.
		/// </returns>
		int GetConnections(bool servers, out IConnection[] found);

		/// <summary>
		/// Returns a string array of all the IP addresses available on the local machine.
		/// </summary>
		/// <returns>Array containing IP addresses.</returns>
		string[] GetLocalAddresses();
	}

	/// <summary>
	/// Allows instantiating internal classes within the Genesis asembly from external code.
	/// </summary>
	public class InterfaceFactory
	{
		/// <summary>
		/// Creates a new instance of the Genesis UDP socket controller.
		/// </summary>
		/// <param name="Name">Optional name for the object.</param>
		/// <returns>An instance of the Genesis UDP socket controller.</returns>
		public static IGenesisUDP CreateGenesisUDP(string Name)
		{
			return new GenesisUDP(Name);
		}
	}
}

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


Written By
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).

Comments and Discussions