Click here to Skip to main content
Email Password   helpLost your password?

Sample Image

Introduction

The Genesis UDP project is a class library that implements a lightweight UDP server and client using the .NET sockets functionality. It uses UDP to keep the amount of data being sent across the network low, and has many features such as basic encryption, sequenced packets, and a reliable channel.

Genesis communicates via "command packets" - a command packet is one or more UDP packets that have a 2 byte opcode, and a variable number of string fields. Unreliable packets can be up to 512 bytes, reliable packets can be longer but are split up by Genesis and sent in sequence. There are a few internal opcodes used by Genesis, but apart form that, how packets, opcodes and fields are handled is totally the responsibility of the host application developer.

There is also an optional encryption system, it is not too advanced but if enabled, will generate a random 320 bit key for each connecting client and use that key in an XOR encryption algorithm. This is quite secure as no two clients share the same key, however it relies on the initial connection packets not being sniffed. Adding public/private key encryption etc. is a possible enhancement to the library. It is possible to be selective over which connections are encrypted and which are not.

Servers vs. Clients

Genesis works on the basis that every instance of Genesis can be both a client and a server. The line between the client and the server is blurred, as any application that uses Genesis can both connect to servers and accept connections from clients. Of course, clients can't just connect by default - the appropriate events must be hooked in the host application to enable the functionality. A server is defined as the remote host that accepted the connection, whilst a client is the remote host that initiated the connection. As an instance of Genesis can do both, it can be both a server and client to other Genesis instances. Servers and clients are known collectively simply as hosts.

The diagram above shows how the idea of clients and servers works in Genesis. Each blue box represents an instance of the Genesis library - none are specifically designated servers or clients as both can potentially accept and initiate connections. The definition of server and client is only valid in the context of a single Genesis instance. Let us consider "Genesis 1", it is connected to servers 2 and 3, and 4 is connected as a client, this is determined by the directions of the arrows (the arrows represent which box initiated the connection). If we look at the perspective of "Genesis 2", we see there are just two clients, 1 and 3. However, if we look at the perspective of "Genesis 4", there is one server, "Genesis 1". Notice how "Genesis 1" can be both a client or server depending upon the context.

Background

The idea for Genesis was based on looking at the network protocols of the various online FPS games such as Quake and Half-Life, which use UDP to allow fast communication between the game server and its connected clients. These implementations have the option to send reliable and sequenced packets - abilities which are also incorporated into Genesis. The original intention for Genesis was to be the start of an underlying network API for a game engine written in .NET, however its potential is so great it has been given to the community as a standalone project.

Using the code

There are three interfaces that must be used to implement the Genesis functionality. These are:

The IConnection objects hold information about each connection to a remote host, whether that host is a server or a client. ICommand holds information about a single command packet, including the opcode and data fields. IGenesisUDP is the actual communications class that controls all of the functionality.

Included with the source are two projects "GenesisChatServer" and "GenesisChatClient". These are a pair of projects that implement a chat system similar to IRC. Using these projects as a reference point should help with understanding the Genesis classes.

Creating A Server

Let's look at the GenesisChatServer - this project shows how Genesis can act as a server to serve other instances of Genesis (the clients).

First, we need to declare and instantiate the Genesis object in the host application.

private GenesisCore.IGenesisUDP m_UDP;

... 

m_UDP = GenesisCore.InterfaceFactory.CreateGenesisUDP("ChatServer");

Notice how the class InterfaceFactory was used to create an instance of Genesis.

The GetLocalAddresses method is used to return a list of local IP addresses on the current machine - and is used to populate a combo box in the chat server application.

string[] addresses = m_UDP.GetLocalAddresses( );
...

In order to handle the Genesis communication events, they must be hooked as per the code below:

//Hook genesis core events

m_UDP.OnListenStateChanged += new ListenHandler(m_UDP_OnListenStateChanged);
m_UDP.OnConnectionAuth += new ConnectionAuthHandler(m_UDP_OnConnectionAuth);
m_UDP.OnCommandReceived += new IncomingCommandHandler(m_UDP_OnCommandReceived);
m_UDP.OnConnectionStateChanged += new 
   ConnectionStateChangeHandler(m_UDP_OnConnectionStateChanged);

OnListenStateChanged is called when the state of the Genesis communication changes, it can be in one of two states: "Listen" or "Closed", which is just effectively like enabling or disabling the communication. When Closed, Genesis will drop all remote connections and close the socket.

OnConnectionAuth is called when a client has sent authorization information - this is where the client can be rejected if, for example, the logon credentials are not accepted. In the chat server example, the connection is rejected if the nickname is too short or if the server password is incorrect. Notice how a rejection reason can be sent back to the client. Everything is controlled by modifying the ConnectionAuthEventArgs object sent with the event. The command containing the authorization information can be accessed by the AuthCommand property of the event arguments.

private void OnConnectionAuth(object o, ConnectionAuthEventArgs e)
{
    ... 
 
    if(e.AuthCommand.Fields[1].Length < 3)
      {
        e.AllowConnection = false;
        e.DisallowReason = "Nickname too short.";
        return;
      }
      else if(e.AuthCommand.Fields[1].Length > 15)
      {
        e.AllowConnection = false;
        e.DisallowReason = "Nickname too long.";
        return;
        }

    ...
 }

OnCommandRecieved is called when a remote host sends a command to Genesis. The eventargs for this event allows access to the ICommand object interface (via the SentCommand property) that contains the information relating to the command sent, and allows access to the data fields and opcode. There is also the ability to get the object corresponding to the remote host that sent the command (via the Sender property). This event is only fired from authorized hosts. The chat server uses this event to handle incoming chat messages and user list requests.

OnConnectionStateChanged is fired when a remote host connects or disconnects from the Genesis instance. The eventargs contain information regarding the host, whether it connected or disconnected, and a disconnection reason if the latter. The chat server uses this event to send a connected user list to the newly connected clients.

Creating A Client

Let's look now at the client side of the chat project, "GenesisChatClient". This is similar to the server application in that it instantiates an instance of Genesis and hooks up various events, however some new events are hooked:

m_UDP.OnListenStateChanged += new ListenHandler(m_UDP_OnListenStateChanged);
m_UDP.OnLoginRequested += new SendLoginHandler(m_UDP_OnLoginRequested);
m_UDP.OnAuthFeedback += new AuthenticatedHandler(m_UDP_OnAuthFeedback);
m_UDP.OnConnectionStateChanged += new 
  ConnectionStateChangeHandler(m_UDP_OnConnectionStateChanged);
m_UDP.OnCommandReceived += new IncomingCommandHandler(m_UDP_OnCommandReceived);
m_UDP.OnConnectionAuth += new ConnectionAuthHandler(m_UDP_OnConnectionAuth);
m_UDP.OnSocketError += new SocketErrorHandler(m_UDP_OnSocketError);
m_UDP.OnConnectionRequestTimedOut += new 
  RequestTimedOutHandler(m_UDP_OnConnectionRequestTimedOut);

OnLoginRequested is triggered when the server requests the login details from the client. The client must send a command packet back to the server with the opcode OPCODE_LOGINDETAILS and the appropriate data fields. In the chat sample, this packet contains the user's nickname and the server password:

private void OnLoginRequested(object o, LoginSendEventArgs e)
{
    if(e.Connected)
    {
        e.ServerConnection.SendUnreliableCommand(0, 
               GenesisConsts.OPCODE_LOGINDETAILS, 
               new string[] {txtServerPW.Text, txtNickName.Text} );
        spState.Text = "Sending login details...";
    }
    else
    {
        spState.Text = "Connection rejected - " + e.Reason;
    }
}

The Connected property of the LoginSendEventArgs object is false if the server is unable to accept the connection, for example, if it has reached the maximum capacity. The reason for the rejection is also accessible if needed, in the Reason property.

OnAuthFeedback is triggered when the server has made a decision on whether or not to accept the connection. The eventargs contains a value that determines whether the auth succeeded, and the reason for the failure if otherwise.

OnConnectionAuth is fired when a remote host tries to connect to Genesis, remember this is used in the chat server for authenticating remote hosts. The chat client can accept no connections (as it is acting as a client), so a small piece of code is entered here to reject the connection and send a rejection reason back. If the event was not hooked at all by the client application, the connection would still be rejected, but no reason would be sent to the host attempting to connect.

private void m_UDP_OnConnectionAuth(object o, ConnectionAuthEventArgs e)
{
    //Clients don't accept connections.

    e.AllowConnection = false;
    e.DisallowReason = "Can't connect directly to a chat client";
}

One thing of importance is how connections are established from the client. It starts in the chat client with the following code:

/// <summary>

/// Connect to server button was clicked

/// </summary>

private void btnConnect_Click(object sender, System.EventArgs e)
{
    server_ip = txtServerIP.Text;
    m_UDP.RequestConnect(ref server_ip, 
       Convert.ToInt32(txtServerPort.Text), 
       out server_req_id);
    spState.Text = "Connecting...";
    btnConnect.Enabled = true;
}

The RequestConnect method handles initiating the connection. Notice that the server IP parameter is by reference; this is because it is possible to pass the method a host name (rather than an IP address), but the IP will be resolved and the actual string will be changed to the IP address. This allows the host application to take advantage of the hostname resolution, and is required when using other functions within Genesis (as many of the connection search functions will only accept the remote host IP address). The other parameter to note is the last parameter, which is an out parameter and returns the connection request ID. This ID is a number unique to all connection requests and allows a connection request to be cancelled via the CancelConnect method.

Note that establishing a connection is an asynchronous operation, and to catch whether a connection has succeeded requires the use of two events, OnConnectionRequestTimedOut and OnConnectionStateChanged. If the connection attempt times out, the former event is fired. If the connection attempt succeeds, then the latter is fired with arguments that show a connection has been established. Both of these events allow access to the remote host address and the connection request ID generated at the call to RequestConnect to allow differentiation between different connections and connection requests. This can be seen implemented in the GenesisChatClient project.

Sending Data to Remote Hosts

Genesis wraps this functionality in very easy to use method. Two of the methods reside in the IConnection object, these are shown below:

int SendUnreliableCommand(byte flags, string opcode, string[] fields);
int SendReliableCommand(byte flags, string opcode, string[] fields);

These two methods allow sending a single command packet to the remote host associated with the IConnection object. The opcode and fields can be any data the host application recognizes, valid values for the flags are given below (from Constants.cs):

//Command packet flags

public static byte FLAGS_NONE = 0;
public static byte FLAGS_CONNECTIONLESS = 1;
public static byte FLAGS_ENCRYPTED = 2;
public static byte FLAGS_COMPOUNDPIECE = 4;
public static byte FLAGS_COMPOUNDEND = 8;
public static byte FLAGS_RELIABLE = 16;
public static byte FLAGS_SEQUENCED = 32;

Of the flags available in the Constants.cs file, only one should ever be used manually in the method calls, and that is FLAGS_SEQUENCED which applies a sequencing to unreliable packets. The other flags are automatically set by Genesis and should not be changed by the host application.

It is possible to broadcast a command packet to multiple remote hosts using the following methods in the IGenesisUDP interface:

int SendUnreliableCommandToAll(BroadcastFilter filter, 
                  byte flags, string opcode, string[] fields);
int SendReliableCommandToAll(BroadcastFilter filter, 
                  byte flags, string opcode, string[] fields);

These methods work exactly the same as the first two, except they take broadcast filter flags, which are defined in Constants.cs as:

[Flags]
public enum BroadcastFilter : int
{
    None     = 0,  //Filter out everything

    Servers  = 1,  //Send to servers we are connected to.

    Clients  = 2,  //Send to clients connected to us.

    All      = Servers | Clients,
                  //Send to both servers

                  //and clients (every connection).

    AuthedOnly = 4,//Only send to authed clients or servers we are authed with.

}

This allows the broadcast to be limited to servers, clients, and optionally only those connections that have successfully been authenticated.

Points of Interest

It is important to note that the events in Genesis are thrown on a separate thread - not the UI thread. This means that the Invoke method is needed to pass the event to the UI thread, if changing UI elements is your goal. The chat server uses this technique to change the UI based on event data.

When the StopListen method is called, Genesis automatically sends out a disconnection packet to all of the remote hosts connected to it to inform them that it is shutting down.

The only method within Genesis that can take a hostname instead of an IP is RequestConnect. All other methods need an explicit IP address.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Questioncan it be customize for private chat ?
anil_mane
4:34 17 Nov '09  
Do you have feature of private chat in this ?
and I think flash is also needs some work.


But overall project is excellent.

anil
anilmane@hotmail.com

Generalprogramming unblocking udp....
raju4756
4:26 17 Sep '09  
hi

how to get the programme to be in a running state..
even with unblocking techniques using select()..
for instance using the code...

fd_set socks;
struct timeval t;
FD_ZERO(&socks);
//FD_SET(sock, &socks);
FD_SET(Recv_Socket, &socks);
t.tv_sec = timeoutinseconds;
if (select(Recv_Socket + 1, &socks, NULL, NULL, 0))
{
recvfrom(Recv_Socket, Recv_Buf, Recv_BufLen, 0, (struct sockaddr *) &Sender_Addr, &Sender_AddrSize);
} //with timeinterval set to 100 or 10;

the programm still hangs.. waiting for the incoming message... as we have other process running other than just recieving msg(like menu handling, etc..) i should be able to proceed with my execution even if there is no msg to be recieved.... (im still recieving the msg whenever the msg arrives..)....
GeneralRe: programming unblocking udp....
raju4756
21:05 17 Sep '09  
ok ...

i got that trick..

just setting timeinterval to zero..... works fine.... Smile
QuestionAny java and C++ code for genesis?
Phil. Roy
12:57 28 Jan '09  
Since I am in the middle of a project that is c# on one end and java on the other, are you aware of a port of Genesis that would have been made in other languages than C# while staying compatible with the other?
AnswerRe: Any java and C++ code for genesis?
Topchris
6:44 24 Oct '09  
I know this might be a little late for your project, but it seems that the simplest way would be to add a second method similar to send userlist, that would instead send a list of rooms, and what (if any) their passwords are. From there, you can use a hashtable to store the list of chatrooms and their keys on the server, and a second hashtable to store a list of connected users, and the key for the room they are in. Whenever a user sends a chat message, do a lookup for the key, and rebroadcast only to the users within that room.

You'll have to come up with some good methods for joining and leaving rooms.
GeneralPrivate Chatroom
Shawn128
6:16 15 Jun '08  
I just wondering how to add a private chat room to it?

Please advise!
Generaludp receiver problem when udp receiver is behind router
hafidz
22:53 11 Feb '08  
LAN To LAN
i have 2 program which using udp technology for both send program and receive program.This two program work very well on LAN connection means both sender and receiver is in same connection.On lan setting i used port 11000 and ip is a fixed ip.

Lan to Outside world
But when i try it on the internet with same port for lan setting,sender is in private network(lan) and i try to send msg to receiver(outside world) where receiver is using dynamic ip address(behind router) it seems the destination pc cannot receive any message from the udp sender.

I'm sure there is no firewall blocking.i already tried winsock still cannot.i was not allowed to do NAT also not allowed to do DYNDNS. Anyone who have better solution please help me i'm working with this problem for 6 month

i'm using vb.net 2003
GeneralChat Conference
tempsh
0:06 22 Aug '07  
Very nice article
Can this be used to send voice?
That is, I need to let many users connect to the server and hear the voice but only one user can speak at any time. I mean, each user can be put on a queue and speak for a few minutes and then the next user will speak.
Any idea is appreciated cause I really need to complete my project soon, though I'm beginner in this fieldFrown
GeneralAsynchronous TCP library - similary designed
Petr Antos
0:54 9 Jul '07  
Take a look at very similar project for TCP communication too; If you need short TCP roundtrips and maintain connection (sessions) between them, this is also handled by this thing:
http://www.codeproject.com/cs/internet/AsyncSocketServerandClien.asp[^]

I am using both of this libraries and I am very happy with them Smile

Petr Antos

GeneralRUDP library
cdemez
5:55 21 May '07  
cool article...

I even propose you to use the RUDP library : www.codeplex.com/RUDP

Wink

Krys

QuestionMono?
d_m_r
8:28 17 May '07  
Does it work with Mono on Linux??
AnswerRe: Mono?
Petr Antos
0:44 9 Jul '07  
take a try Smile, i have compiled this easily for Compact Framework with very few and easy changes

Petr Antos

Generalpleaseee
chiro__farfousha
15:31 30 Apr '07  
Please can you help me in my graduation project.
My project is to send a real video from a computer to a pocket PC.
I am just want to know the function that the server is knowing now that there is a client enter now to put in this function capturing video streaming to send it for client..
PLEASE can you reply for me. I wamt to know it now plssss
QuestionPeer-to-peer in single application?
danenglish2
11:23 9 Mar '07  
Great little UDP library, Rob!! I'm looking at your client/server example, but I was wondering about the best way to implement peer-to-peer in a single application where anyone can be the "server". Would I need two instances of Genesis in my app (one for client, one for server), or could a single instance handle it?

Thanks,
Dan English
Loud Dragon Games

Generalit doesn't work in net
dark_magician_6
23:09 25 Feb '07  
Nice work but it doesn't work in net.I allowed the UDP port and it still doesn't work in net. So i have found a code that works in net but i don't know where to put it. So please i need to know where to fix the problem with putting this code somewhere or how to fix the firewall (ZoneAlarm). My e-mail is d_magician_6@msn.com or jurko007@gmail.com

TcpListener listener;
IPAddress ipaAddress;
int iPort;

ipaAddress = IPAddress.Parse(ipAddress); ;
iPort=int.Parse(port);

IPEndPoint endPoint=new IPEndPoint (ipaAddress, iPort);

listener = new TcpListener (endPoint);
listener.Start();
GeneralGreat library! But sending byte arrays?
Rynus
3:21 20 Nov '06  
Hey, first I want to say that your library works excellent. It's easy, and native C#. Great.
But I'm working on a freeware multiplayer game, and I need to send byte arrays (serialized objects). It seems that this isn't possible with your library, since your sending methods only allow string arrays :'(.
Is it possible, or easy to work around? Since converting a byte array to a string means loss of data (non-ascii chars).

-Yours, Rynus Rein
GeneralRe: Great library! But sending byte arrays?
DragonD7660
20:44 18 Dec '06  
You can send byte[] as strings by just converting them. eg:

byte[] array;
string str;
str = System.Text.Encoding.Default.GetString(array);

on the other end...
array = System.Text.Encoding.Default.GetBytes(str);





-Dragon
GeneralRe: Great library! But sending byte arrays?
Rynus
3:01 19 Dec '06  
I already tried that... But strange enough, some bytes aren't converted to a character, like a 1-byte or a 0-byte.
Generalcan work over the net
mos666
21:14 23 Jun '06  
could u make it work in wna instead of lan that would great for contact mostafa_mcsd2006@hotmail.comSmileSmile
GeneralRe: can work over the net
Eric Engler
7:59 5 Jul '06  
You just have to make sure your firewall will pass UDP traffic for the specified port. That should be all that's needed. This isn't something most companies would be interested in doing unless there's a business requirement for this kind of thing.
GeneralWire up events in vb.net
kuhiuhiu
19:48 9 Jun '06  
Anyone know vb equivalent of c#:

m_UDP.OnConnectionAuth += new ConnectionAuthHandler(m_UDP_OnConnectionAuth);

I'm doing a request.connect, but the events never fire.... any ideas, here is what i'm using....

--------------------------------------------
Public m_UDP As GenesisCore.IGenesisUDP = GenesisCore.InterfaceFactory.CreateGenesisUDP("test")

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim server_ip As String = "10.10.12.99"
Dim server_req_id As String = ""
AddHandler m_UDP.OnConnectionStateChanged, New GenesisCore.ConnectionStateChangeHandler(AddressOf tt)
AddHandler m_UDP.OnConnectionRequestTimedOut, New GenesisCore.RequestTimedOutHandler(AddressOf jj)
Dim f As Integer = m_UDP.RequestConnect(server_ip, 10011, server_req_id)
End Sub

Sub tt(ByVal o As Object, ByVal e As GenesisCore.ConnectionStateChangeEventArgs)
MsgBox("t")
End Sub
Sub jj(ByVal o As Object, ByVal e As GenesisCore.RequestTimedOutEventArgs)
MsgBox("j")
End Sub
--------------------------------------------

Any ideas ?
Thanks.
Rob.
Questionit doesn,t work
mos666
16:03 4 Jun '06  
hi dear sir's i congratulate for this wonderful work but it works only on the lan how can i make it work on the net thanks scencerly yours

mos666_mcsd2006@hotmail.com
GeneralMAX_FIELD_LEN and MAX_FIELDS
Tobias Hertkorn
7:53 29 May '06  
Hi Rob,

first of all thanks for the great work!

I am having a bit of fun with your library and I just wondered if there are any limitations on MAX_FIELD_LEN and MAX_FIELDS in GenesisConsts. Why are they set to 2000 and 50? Performance reasons? Any reason not to set both to int.MaxValue. Big Grin Just kidding.

When constructing the message header it seems as if "only" short.MaxValue could be supported. Is there any reason not to set MAX_FIELD_LEN = short.MaxValue and MAX_FIELDS something like 500 (or short.MaxValue as well)? I know those values would allow for huge messages. But I would never hit both limitations simultaniously. For one specific message I need a lot of very short fields and for a different one only very few fields, but one of them might be quite big...

Thanks again!

Cheers,
Tobi

---
Tobi + C# = T#
GeneralRe: MAX_FIELD_LEN and MAX_FIELDS
DragonD7660
20:49 18 Dec '06  
The max field len and max fields are set like that so that you don't go over the max size of any packet. Genesis will send reliable commands as only one packet. Unreliable commands are just split up according to the max fields setting. to the best of my knowledge udp packets are limited to a packet size of 65535. but some routers and gateway may not like that. you can change them but do so wisely.


-dragon
GeneralBAN_CHECK_TIME?
DragonD7660
20:04 28 May '06  
While looking through the source code i found BAN_CHECK_TIME. Which is not used anywhere! Any reason why?



-Dragon


Last Updated 21 Dec 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010