Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

FreeDOM (programming)

4.63/5 (18 votes)
15 Dec 2007CPOL8 min read 7   641  
(Free Document Object Model) is a web development technique used for creating event driven web applications. FreeDOM is designed to overcome the limitations of stateless HyperText Transfer Protocol (HTTP) transmissions.

Please be patient, more source files and demos coming soon!

Introduction

What is FreeDOM? Well, FreeDOM is a new concept I created that changes a webpage into a fully asynchronous event driven application by using currently available technologies. Below, I will compare FreeDOM with currently working Web 2.0 concepts, and show you the benefits of switching to this architecture.

Web 2.0 Techniques

There are many ways of creating web pages that go and get content from servers and update themselves. Below is a list of the most common ways:

  1. AJAX - An ActiveX object built into browsers that sends and receives text or XML HTTP requests.
  2. IFrame - A built-in browser element control that can send and request webpage content similar to AJAX.
  3. Comet - A concept of leaving a webpage in a state of constant downloading that simulates an HTTP push technology, to receive data events.
  4. HTTP Streaming - Using third party technologies such as Java Applets or ActiveX to stream data from a server to a client.

Most of these concepts come with plenty of downfalls to true application state. Both AJAX and IFrames are very resource happy. Every time you want to get data from the server, you must create an object or element and send an HTTP Request. This causes the HTTP server to have huge amounts of requests eating up memory and processor time. There is also a delay in the response time due to the header information being passed back and forth. Also, AJAX and IFrames do not know if there is data available; they have to send a request just to find out if it is available, and once again causing a lot of unnecessary talking between the server and the client. Another issue with HTTP Requests is that it fills up your server logs. This can be a headache for servers with large amounts of traffic. Extremely large logs take up processor time, hard-drive space, and backup space. Also, AJAX/IFrames can only receive text or XML. This can be very irritating because in order to use it on a page, you must convert it into a usable format. A lot of websites use XML objects backend and JSON objects front end; in doing this, they must have a conversion process. This is at another memory and processor expense.

Screenshot - ajaxprocessing.gif

A lot of developers have noticed these flaws in AJAX and IFrames, and have started to invent new technologies. One being Comet, another cleansing name. Comet is very hard to implement, and comes with a lot of unknown bugs. Basically, in order to use it, you usually have to create a custom web server, because most servers today were not made to leave requests in an open state. Most servers have timeouts and caching built in to make them faster for a get in get out way of doing business. Comet is a step up from AJAX, but it's not the cure. Comet can only receive asynchronous events, it cannot talk back to the server. You need to implement Comet to receive and AJAX to send, making Coment dependant on AJAX. Another downside of using Comet is that browsers also have an issue with a never ending webpage. Have you ever been on a page and it locks up causing all your other browsers windows to close? This is caused by the EXE sharing memory and thread space. So, Comet is a great idea, but it is not feasible due to drawbacks in the current technologies.

Screenshot - cometprocessing.gif

Another technology is HTTP Streaming. There are many different versions of HTTP streaming. One way is setting up a server to talk back and forth to an ActiveX or Java Applet. Very hard to implement and maintain, with many security and licensing issues for both the client and the server. Most users will not install your code because of fears of corrupt code that they will not be able to remove, plus cause damage to their Operating System or files. This can be very scary to the end user.

In the next section, I will describe FreeDOM and how it will fix these issues.

FreeDOM!

So, how would one go about fixing these old technologies to make websites and applications one? Should we wait for large companies to create better working browsers, or should we invent it now with the currently working technologies? As a developer, I feel I should wait... but as an inventor, I have decided to make it work. What I want is the freedom to do what I want and build what I want on a webpage. I came up with the name FreeDOM for this reason, plus it is really what we want to do. We want to be able to work with the browser DOM freely and update it whenever we choose. So, I asked myself what current technologies could I use to create a true event state application. One that came to mind was Flash, with so much new hype around AIR and Flex applications. It's hard not to notice a big boom in Flash usage everywhere since Adobe took over it from Macromedia. Some of the latest statistics say Flash is installed on 90% or more of machines world-wide across Operating Systems and browsers. This got me thinking! Could I use Dlash in some way with my currently working webpage technology? Flash seems to work well for creating banners and animations, but could it handle passing data back and forth? After some testing with ActionScript 3.0, I figured out a way to replace AJAX, IFrames, Comet, and HTTP Streaming. I am sure though you could probably use Silverlight the same way, which I am currently looking into, maybe some MS guys could help figure that out for me.

How Does FreeDOM Work?

FreeDOM works by using Flash Binary Sockets to communicate with your custom socket server. To pass binary data from Flash sockets to your JavaScript webpage, you need a custom class called ExternalInterface. This class will allow you to make calls to Flash methods and add callbacks for your JavaScript methods. So, let's see some code so you will understand.

Screenshot - FreeDOMProcessing.gif

Flash SWF Code

Add the following code to a document class .AS file. It is a wrapper for the custom JSocket class and the main movie clip. Please keep in mind to keep the size of this and your jsockets small. By keeping them small, you can load everything in the first preloader frame and bypass the activate SWF message.

Java
package {
 //Imports--------------------

 //For communicating with javascript
 import flash.external.ExternalInterface;

 import JSockets.JSocket;//JSocket

 import flash.display.MovieClip;//Movie Clip

 import flash.system.Security;//For Domain Security

 
 import flash.text.TextField;
 import flash.text.TextFieldAutoSize;
 import flash.text.TextFormat;
 //---------------------------

 //Main Class for creating javascript sockets

 public class Main extends MovieClip {
  //Create Properties----------------

  private var SocketArray:Array;
  //---------------------------------

  
  //Constructor

  public function Main() {
     
   //Add Javascript EntryPoints-------------------

   ExternalInterface.addCallback("CreateSocket", this.CreateSocket);
   ExternalInterface.addCallback("Connect", this.Connect);
   ExternalInterface.addCallback("Close", this.Close);
   ExternalInterface.addCallback("Flush", this.Flush);
   ExternalInterface.addCallback("Send", this.Send);
   //---------------------------------------------

  }
  //Creates a socket for use in javascript

  public function CreateSocket(_ID:String, _Cls:String, _Con:String,
 _Ioe:String, _Se:String, _Dat:String):void {
   var NS:JSocket = new JSocket();
   NS.ID = _ID;
   NS.CloseEventCallback = _Cls;
   NS.ConnectEventCallback = _Con;
   NS.IOErrorEventCallback = _Ioe;
   NS.SecurityErrorEventCallback = _Se;
   NS.SocketDataEventCallback = _Dat;
   this.SocketArray = new Array();
   this.SocketArray.push(NS);
  }
  //Gets the socket object

  private function GetSocket(_ID:String):JSocket {
   for each (var S:JSocket in this.SocketArray) {
    if (S.ID == _ID) {
     return S;
    }
   }
   return null;
  }
  //Connects to a host and port

  public function Connect(_ID:String, _Host:String, _Port:uint):void {
   //Get Working Socket----

   var FS:JSocket = this.GetSocket(_ID);
   //----------------------

   
   if (FS) {
    FS.Connect(_Host, _Port);
   }
  }
  //Closes a connection to a host

  public function Close(_ID:String):void {
   //Get Working Socket----

   var FS:JSocket = this.GetSocket(_ID);
   //----------------------

   if (FS) {
    FS.close();
   }
  }
  //Flushes a connection

  public function Flush(_ID:String):void {
   //Get Working Socket----

   var FS:JSocket = this.GetSocket(_ID);
   //----------------------

   if (FS) {
    FS.flush();
   }
  }
  //Sends data to a connection

  public function Send(_ID:String, _Data:String):void {
   //Get Working Socket----

   var FS:JSocket = this.GetSocket(_ID);
   //----------------------

   
   if (FS) {
    FS.writeUTFBytes(_Data);
   }
  }
 }
}

Next is the JSocket class which wraps the Flash sockets class. Below is the code for that:

Java
package JSockets{
 //Imports--------------------

 import flash.external.ExternalInterface;//For communicating with javascript

 import flash.net.Socket;//Flash Socket Class

 import flash.errors.*;//Flash Errors

 import flash.events.*;//Flash Events

 //---------------------------

 //Actual socket class

 public class JSocket extends Socket {
  //Create Properties---------------

  public var ID:String;
  public var Host:String;
  public var Port:uint;
  public var CloseEventCallback:String;
  public var ConnectEventCallback:String;
  public var IOErrorEventCallback:String;
  public var SecurityErrorEventCallback:String;
  public var SocketDataEventCallback:String;
  //--------------------------------

  //Constructor

  public function JSocket() {
   AttachEvents();
  }
  //Attach Events

  private function AttachEvents():void {
   //Add Event Handlers-------------------------------------------------------

   addEventListener(Event.CLOSE, Handler_Close);
   addEventListener(Event.CONNECT, Handler_Connect);
   addEventListener(IOErrorEvent.IO_ERROR, Handler_IOError);
   addEventListener(SecurityErrorEvent.SECURITY_ERROR, Handler_SecurityError);
   addEventListener(ProgressEvent.SOCKET_DATA, Handler_SocketData);
   //-------------------------------------------------------------------------

  }
  //Connects to a host and port

  public function Connect(_Host:String, _Port:uint):void {
   Host = _Host;
   Port = _Port;
   //Call socket connect---

   connect(Host,Port);
   //----------------------

  } 
  // Handles the close event

  private function Handler_Close(event:Event):void {
   ExternalInterface.call(this.CloseEventCallback, event.type);
  }
  // Handles the connect event

  private function Handler_Connect(event:Event):void {
   ExternalInterface.call(this.ConnectEventCallback, event.type);
  }
  // Handles if io error event

  private function Handler_IOError(event:IOErrorEvent):void {
   ExternalInterface.call(this.IOErrorEventCallback, event.type, event.text);
  }
  // Handles security error event

  private function Handler_SecurityError(event:SecurityErrorEvent):void {
   ExternalInterface.call(this.SecurityErrorEventCallback, 
                          event.type, event.text);
   
  }
  // Handles socket data event

  private function Handler_SocketData(event:ProgressEvent):void {
   ExternalInterface.call(this.SocketDataEventCallback, event.type, 
                          this.readUTFBytes(this.bytesAvailable));
  }
 }
}

There is a lot of tweaks you can do to these classes to customize them to your needs. These are just simple examples that I got my test app to work with. Keeping the Flash as a simple byte router is the best solution.

JavaScript Implementation

Let's see how to use these classes in JavaScript. The code below is for the FreeDOM chat program example. It simulates a custom personal IRC. If the page is not up, its because I'm updating it. Here is the code for that example. Please keep in mind, this is a simple test, it doesn't support validation or bugs, use at your own risk.

JavaScript
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd<html xmlns="
http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml

<head>
    <title>Test</title>
</head>
<script type="text/javascript">
function Connect()
{
 var f = thisMovie("flashObject");
 f.CreateSocket("id1", "CloseEventCallback", 
"ConnectEventCallback", "IOErrorEventCallback", 
"SecurityErrorEventCallback", "SocketDataEventCallback");
 f.Connect("id1", "http://www.codevendor.com/">www.codevendor.com}
function DisConnect()
{
    var f = thisMovie("flashObject");
    f.Send("id1", "Logout: \n");
    f.Flush("id1");
}
function thisMovie(movieName) 
{
    return (navigator.appName.indexOf("Microsoft") != -1)?
window[movieName]:document[movieName];
}
function SendMessage()
{
    var M = document.getElementById("Message").value;
    document.getElementById("Message").value = "";
    var f = thisMovie("flashObject");
    f.Send("id1", "MESSAGE: " + M + "\n");
    f.Flush("id1");
}
function SendCommand()
{
    var M = document.getElementById("CommandBox").value;
    document.getElementById("CommandBox").value = "";
    var f = thisMovie("flashObject");
    f.Send("id1", M + "\n");
    f.Flush("id1");
}
function CloseEventCallback(T)
{
    document.getElementById("Status").value+=" -> " + T + "\n";
    
    ScrollToBottom();
}
function ConnectEventCallback(T)
{
    document.getElementById("Status").value+=" -> " + T + "\n";
    ScrollToBottom();
    GetUsers();
}
function GetUsers()
{
    var f = thisMovie("flashObject");
    f.Send("id1", "USERS: ");
    f.Flush("id1");
}
function IOErrorEventCallback(T, E)
{
    document.getElementById("Status").value+=" -> " + T + "\n";
    document.getElementById("Errors").innerHTML+=" -> " + E + "\n";
    
    ScrollToBottom();
}
function SecurityErrorEventCallback(T, E)
{
    document.getElementById("Status").value+=" -> " + T + "\n";
    document.getElementById("Errors").innerHTML+=" -> " + E + "\n";
    
    ScrollToBottom();
}
function SocketDataEventCallback(T, D)
{
    eval("D2 = " + D);
    if(D2.CMD == "USERLIST" || D2.CMD == "LOGOUT")
    {  
        document.getElementById("Users").value = D2.DATA.split(",").join("\n"); 
        document.getElementById("ChatRoom").value+= D2.STAT + "\n";
    }
    if(D2.CMD == "MESSAGE")
    {
        document.getElementById("ChatRoom").value+= D2.STAT + "\n";
    }
    
    document.getElementById("Status").value+=" -> " + T + "\n";
    document.getElementById("Data").value+=D + "\n";
    
    ScrollToBottom();
}
function ScrollToBottom()
{
    document.getElementById("ChatRoom").scrollTop =
 document.getElementById("ChatRoom").scrollHeight;
    document.getElementById("Status").scrollTop = 
document.getElementById("Status").scrollHeight;
    document.getElementById("Data").scrollTop = 
document.getElementById("Data").scrollHeight;
    document.getElementById("Errors").scrollTop =
 document.getElementById("Errors").scrollHeight;
}
</script>
<body>
    <table cellpadding="0px" cellspacing="10px">
        <tr>
            <td valign="top"><strong>Client Chat</strong><br /><br />
                <input type="button" onclick="Connect();"
 name="Connect" value="Connect" />  <input type="button" onclick="DisConnect();" 
name="DisConnect" value="DisConnect" />
               <br /><br /><table><tr><td>Chat Room</td>
               <td>Users</td></tr>
               <tr><td><textarea id="ChatRoom" rows="20" cols="40">
               </textarea></td><td><textarea 
id="Users" rows="20" cols="20"></textarea></td></tr></table> Message:
                <input type="text" id="Message" style="width:230px;" />
<input type="button" onclick="SendMessage();" name="Send"
                    value="Send" />  CMD: 
<input type="text" id="CommandBox" style="width:80px;" />
<input type="button" onclick="SendCommand();" name="Issue"
    value="Issue" /><br />
            </td>
            <td valign="bottom"><strong>Socket Events</strong><br /><br />
                Status<br />
                <textarea id="Status" rows="5" cols="30"></textarea>
                <br />
                Data<br />
                <textarea id="Data" rows="5" cols="30"></textarea>
                <br />
                Errors<br />
                <textarea id="Errors" rows="5" cols="30"></textarea></td>
        </tr>
    </table>
    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 
        id="flashObject" width="0" height="0" 
        codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"
        style="display: hidden">

        <param name="movie" value="javascriptsockets.swf" />
        <param name="quality" value="low" />
        <param name="allowScriptAccess" value="always" />
        <embed src="javascriptsockets.swf" quality="low" 
            width="0" height="0" name="flashObject"
            id="flashObject" align="middle" play="true" 
            loop="false" allowscriptaccess="always"
            type="application/x-shockwave-flash" 
            pluginspage="http://www.macromedia.com/go/getflashplayer%22%3Cbr%3E">
                        http://www.macromedia.com/go/getflashplayer"
            style="display: hidden">
          </embed>
    </object>
</body>
</html>"
", 81);
">
">

Socket Server Implementation

I won't go into the full details of the code, but it's a simple async socket server created in C#. It handles the incoming connection requests and sends out responses. The important things to look at is the Sandbox Cross Domain Policy it sends out. Basically, Flash will request this from your server to make sure it's allowed to use your server. The code for this is given below:

C#
/// <summary>Class for creating a tcp socket listener.</summary>

class Listener
{
    #region Variables
    private ManualResetEvent vManualReset = null;
    private Dictionary<Guid, StateObject> vStateObjects = null;
    private List<Guid> vStateIDS = null;
    private Socket vListenerSocket = null;
    #endregion Variables
    #region Properties
    /// <summary>Holds the current state ids.</summary>

    public List<Guid> StateIDS
    {
        get { return vStateIDS; }
        set { vStateIDS = value; }
    }
    /// <summary>Holds all currently working
    /// socket stateobject information.</summary>

    internal Dictionary<Guid, StateObject> StateObjects
    {
        get { return vStateObjects; }
    }
    /// <summary>The main listener socket.</summary>

    public Socket ListenerSocket
    {
        get { return vListenerSocket; }
    }
    #endregion Properties
    #region Constructor
    /// <summary>Constructor</summary>

    public Listener()
    {
    }
    #endregion Constructor
    #region Server Methods
    /// <summary>Listen for connections.</summary>

    public void Listen()
    {
        //Declare variables--------------

        IPHostEntry Host = null;
        IPEndPoint EP = null;
        IPAddress IP = null;
        //-------------------------------

        //Set Host and Endpoint----------

        IP = IPAddress.Parse("127.0.0.1");
        if (IPAddress.TryParse(
            ConfigurationManager.AppSettings["ListeningAddress"], out IP))
        {
            EP = new IPEndPoint(IP, 
                 int.Parse(ConfigurationManager.AppSettings["ListeningPort"]));
        }
        else
        {
            Host = 
              (ConfigurationManager.AppSettings["ListeningAddress"] == 
              string.Empty) ? 
              Dns.GetHostEntry(ConfigurationManager.AppSettings["ListeningAddress"]) : 
              Dns.GetHostEntry(Dns.GetHostName());
            EP = new IPEndPoint(Host.AddressList[0], 
                 int.Parse(ConfigurationManager.AppSettings["ListeningPort"]));
        }
        //-------------------------------

        //Create listener and stateobject holder----------------

        vManualReset = new ManualResetEvent(false);
        vListenerSocket = new Socket(EP.Address.AddressFamily, 
                          SocketType.Stream, ProtocolType.Tcp);
        //------------------------------------------------------

        try
        {
            //Bind Socket and start listening----

            vListenerSocket.Bind(EP);
            vListenerSocket.Listen(int.Parse(
              ConfigurationManager.AppSettings["TotalAllowedConnections"]));
            //-----------------------------------

            while (true)
            {
                vManualReset.Reset();
                vListenerSocket.BeginAccept(
                  new AsyncCallback(ConnectionAccept), vListenerSocket);
                vManualReset.WaitOne();
            }
        }
        catch { }
        finally
        {
        }
    }
    /// <summary>Accepts the connection and transfers
    /// it and calls listening thread to repeat</summary>

    /// <param name="_AR">The async result.</param>

    private void ConnectionAccept(IAsyncResult _AR)
    {
        //Declare Variables-----------

        Socket ListSocket = null;
        Socket CurrentSocket = null;
        StateObject CurrentStateObject = null;
        //----------------------------

        //Set Sockets-----------------

        ListSocket = (Socket)_AR.AsyncState;
        CurrentSocket = ListSocket.EndAccept(_AR);
        //----------------------------

        //Set ManualEvent-------------

        vManualReset.Set();
        //----------------------------

        //Check if there is stateobjects and add to it----------

        if (vStateObjects == null) { vStateObjects = 
            new Dictionary<Guid, StateObject>(); }
        //------------------------------------------------------

        //Check if stateids is null and add to it---------------

        if (vStateIDS == null) { vStateIDS = new List<Guid>(); }
        //------------------------------------------------------

        //Set Current State Object------------

        CurrentStateObject = new StateObject(CurrentSocket, 
              int.Parse(ConfigurationManager.AppSettings["BufferSize"]));
        //------------------------------------

        //Add StateObject to generic list-----

        vStateObjects.Add(CurrentStateObject.UniqueID, CurrentStateObject);
        //------------------------------------

        //Add ID------------------------------

        vStateIDS.Add(CurrentStateObject.UniqueID);
        //------------------------------------

        try
        {
            //Start Receiving Data----------------

            CurrentSocket.BeginReceive(CurrentStateObject.Buffer, 0,
               CurrentStateObject.BufferSize, 0, new AsyncCallback(ReadCallback), 
               CurrentStateObject);
            //------------------------------------

        }
        catch { CloseSocket(CurrentStateObject); return; }
    }
    /// <summary>Handles the receiving of data from a current socket.</summary>
    /// <param name="_AR">The async result.</param>

    private void ReadCallback(IAsyncResult _AR)
    {
        //Declare Variables-----------

        StateObject CurrentState = null;
        int BytesRead = 0;
        string Command = string.Empty;
        string r = string.Empty;
        bool first = false;
        Guid[] CurrentIDS = new Guid[vStateIDS.Count];
        //----------------------------

        //Copy IDS--------------------

        vStateIDS.CopyTo(CurrentIDS);
        //----------------------------

        //Set StateObject-------------

        CurrentState = (StateObject)_AR.AsyncState;
        //----------------------------

        //Get the total bytes read----

        try
        {
            BytesRead = CurrentState.WorkSocket.EndReceive(_AR);
        }
        catch { CloseSocket(CurrentState); return; }
        //----------------------------

        if (BytesRead > 0)
        {
            //Check for XML Domain Policy Request-------

            Command = Encoding.Default.GetString(CurrentState.Buffer, 0, BytesRead);
            //------------------------------------------

            switch (Command.ToUpper())
            {
                case "<POLICY-FILE-REQUEST/>\0":
                    //Send Cross Domain Policy--------------------------------------------

                    XmlDocument XD = new XmlDocument();
                    Configuration Config = 
                      ConfigurationManager.OpenExeConfiguration(
                      ConfigurationUserLevel.None);
                    ConfigurationSection CS = (ConfigurationSection)
                      Config.GetSection("CrossDomainPolicy/Policy");
                    XD.LoadXml(CS.SectionInformation.GetRawXml());
                    XmlNode XN = XD.SelectSingleNode("Policy");
                    Send(XN.InnerText.Replace("\r\n", "") + "\0", CurrentState, true);
                    //--------------------------------------------------------------------

                    break;
                default:
                    string[] cmd = Command.Split(':');
                    switch (cmd[0].ToUpper())
                    {
                        case "MESSAGE":
                            foreach (Guid ID in CurrentIDS)
                            {
                                if (ID != CurrentState.UniqueID)
                                {
                                    if (vStateObjects.ContainsKey(ID))
                                    {
                                        Send("{CMD:'MESSAGE', STAT:'<" + 
                                         CurrentState.WorkSocket.RemoteEndPoint.
                                         ToString().Split(':')[0] + "> " + 
                                         cmd[1].Trim() + "'}", vStateObjects[ID], false);
                                    }
                                    else
                                    {
                                        vStateIDS.Remove(ID);
                                    }
                                }
                            }
                            Send("{CMD:'MESSAGE', STAT:'<" + 
                              CurrentState.WorkSocket.RemoteEndPoint.
                              ToString().Split(':')[0] + "> " + cmd[1].Trim() + 
                              "'}", CurrentState, false);
                            break;
                        case "NICKNAME": Send("Your nickname has been changed to" + 
                             cmd[1], CurrentState, false); break;
                        case "LOGOUT":
                            foreach (Guid ID in CurrentIDS)
                            {
                                if (ID != CurrentState.UniqueID)
                                {
                                    if (vStateObjects.ContainsKey(ID))
                                    {
                                        if (first) { r += "\n"; } else { first = true; }
                                        r += vStateObjects[ID].WorkSocket.
                                             RemoteEndPoint.ToString().Split(':')[0];
                                    }
                                    else
                                    {
                                        vStateIDS.Remove(ID);
                                    }
                                }
                            }
                            foreach (Guid ID in CurrentIDS)
                            {
                                if (ID != CurrentState.UniqueID)
                                {
                                    if (vStateObjects.ContainsKey(ID))
                                    {
                                        Send("{CMD:'LOGOUT', DATA:'" + r + 
                                             "',STAT:'User Disconnected - " +
                                             CurrentState.WorkSocket.RemoteEndPoint.
                                             ToString().Split(':')[0] + "'}", 
                                             vStateObjects[ID], false);
                                    }
                                    else
                                    {
                                        vStateIDS.Remove(ID);
                                    }
                                }
                            }
                            Send("{CMD:'LOGOUT', DATA:'" + r + 
                                 "',STAT:'User Disconnected - " +
                                 CurrentState.WorkSocket.RemoteEndPoint.
                                 ToString().Split(':')[0] + "'}", 
                                 CurrentState, true);
                            break;
                        case "USERS" :
                            foreach (Guid ID in CurrentIDS)
                            {
                                if (vStateObjects.ContainsKey(ID))
                                {
                                    if (first) { r += ","; } else { first = true; }
                                    r += vStateObjects[ID].WorkSocket.
                                         RemoteEndPoint.ToString().Split(':')[0];
                                }
                                else
                                {
                                    vStateIDS.Remove(ID);
                                }
                            }
                            foreach (Guid ID in CurrentIDS)
                            {
                                if (ID != CurrentState.UniqueID)
                                {
                                    if (vStateObjects.ContainsKey(ID))
                                    {
                                        Send("{CMD:'USERLIST', DATA:'" + r + 
                                             "', STAT:'Welcome User - " + 
                                             CurrentState.WorkSocket.RemoteEndPoint.
                                                          ToString().Split(':')[0] + "'}", 
                                             vStateObjects[ID], false);
                                    }
                                    else
                                    {
                                        vStateIDS.Remove(ID);
                                    }
                                }
                            }
                            Send("{CMD:'USERLIST', DATA:'" + r + 
                                 "',STAT:'Welcome User - " + 
                                 CurrentState.WorkSocket.RemoteEndPoint.
                                              ToString().Split(':')[0] + "'}", 
                                 CurrentState, false);
                            break;
                    }
                    break;
            }
            try
            {
                //Get more data from socket-----------------

                CurrentState.WorkSocket.BeginReceive(CurrentState.Buffer, 0, 
                     CurrentState.BufferSize, 0, 
                     new AsyncCallback(ReadCallback), CurrentState);
                //------------------------------------------

            }
            catch { CloseSocket(CurrentState); return; }
        }
        else
        {
            //Close the connection and remove the object-----

            CloseSocket(CurrentState);
            //-----------------------------------------------

        }
    }
    /// <summary>Closes the socket and disposes of the state object
    /// and removes it from the generic dictonary.</summary>
    /// <param name="_StateObject">The stateobject to remove.</param>
    private void CloseSocket(StateObject _StateObject)
    {
        //Declare Variables---------------
        Guid UniqueID = _StateObject.UniqueID;
        //--------------------------------
        //Close Connection----------------
        if (_StateObject.WorkSocket != null)
        {
            try
            {
                _StateObject.WorkSocket.Shutdown(SocketShutdown.Both);
                _StateObject.WorkSocket.Close();
            }
            catch { }
            finally
            {
                _StateObject.WorkSocket = null;
                _StateObject = null;
            }
        }
        //--------------------------------
        //Remove object from dictonary----
        vStateIDS.Remove(UniqueID);
        vStateObjects.Remove(UniqueID);
        //--------------------------------
    }
    /// <summary>Send string data to the current socket.</summary>
    /// <param name="_StringData">The string to send.</param>
    /// <param name="_StateObject">The stateobject to send the string to.</param>
    /// <param name="_CloseConnection">Closes the connection.</param>
    public void Send(string _StringData, 
           StateObject _StateObject, bool _CloseConnection)
    {
        Send(Encoding.Default.GetBytes(_StringData), 
             _StateObject, _CloseConnection);
    }
    /// <summary>Send string data to the current socket.</summary>
    /// <param name="_ByteData">The byte data to send.</param>
    /// <param name="_StateObject">The stateobject to send the data to.</param>
    /// <param name="_CloseConnection">Closes the connection.</param>
    public void Send(byte[] _ByteData, 
           StateObject _StateObject, bool _CloseConnection)
    {
        _StateObject.Terminate = _CloseConnection;
        try
        {
            _StateObject.WorkSocket.BeginSend(_ByteData, 0, _ByteData.Length, 0,
                         new AsyncCallback(SendCallback), _StateObject);
        }
        catch { CloseSocket(_StateObject); return; }
    }
    /// <summary>Handles the send callback.</summary>
    /// <param name="ar">The async result.</param>
    private void SendCallback(IAsyncResult _AR)
    {
        //Declare Variables-----------
        StateObject CurrentState = null;
        int BytesSent = 0;
        //----------------------------
        //Set StateObject-------------
        CurrentState = (StateObject)_AR.AsyncState;
        //----------------------------
        try
        {
            BytesSent = CurrentState.WorkSocket.EndSend(_AR);
        }
        catch { CloseSocket(CurrentState); return; }
        //Check to see if terminated---
        if (CurrentState.Terminate) { CloseSocket(CurrentState); }
        //-----------------------------
    }
    #endregion Server Methods
}

The full code can be found in the source files. You can create a socket server however you desire with whatever language, as long as it handles requests. You can even choose to use an HTTP Web Server if you want. It all depends on what you want to code for.

Conclusion

Well, that concludes my FreeDOM tutorial. Please check back for updates as I create more apps using this technique. Have fun! Enjoy!

History

  • December 15, 2007 - FWS - FreeDOM Web Server Version 1.0b created.
  • December 13, 2007 - Creating a custom open source Unix web server in C to handle FreeDOM projects.
  • November 15, 2007 - Bought a domain name for FreeDOM: WWW.FDOM.US.
  • November 07, 2007 - Created ActionScript tutorial.
  • November 05, 2007 - Created SourceForge project for FreeDOM.
  • November 02, 2007 - Added JavaScript Sockets source code and added a Wikipedia entry for FreeDOM (programming).
  • November 01, 2007 - Added FreeDOM Ping example and source code.
  • October 30, 2007 - Version 1.0A.

External Links

Terms and Conditions For Use, Copy, Distribution, and Modification

THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)