65.9K
CodeProject 正在变化。 阅读更多。
Home

FreeDOM(编程)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (16投票s)

2007年10月31日

CPOL

8分钟阅读

viewsIcon

126014

downloadIcon

676

(Free Document Object Model) 是一种用于创建事件驱动型 Web 应用程序的 Web 开发技术。FreeDOM 旨在克服无状态的超文本传输协议 (HTTP) 传输的局限性。

请耐心等待,更多源代码和演示即将到来!

引言

什么是 FreeDOM? 嗯,FreeDOM 是我创造的一个新概念,它通过利用现有技术将网页转变为一个完全异步的事件驱动型应用程序。下面,我将把 FreeDOM 与当前运行的 Web 2.0 概念进行比较,并向您展示切换到此架构的好处。

Web 2.0 技术

有许多方法可以创建能够从服务器获取内容并自我更新的网页。以下是最常见方法的列表:

  1. AJAX - 浏览器内置的 ActiveX 对象,用于发送和接收文本或 XML HTTP 请求。
  2. IFrame - 内置的浏览器元素控件,可以像 AJAX 一样发送和请求网页内容。
  3. Comet - 一种将网页保持在持续下载状态的概念,模拟 HTTP 推送技术,以接收数据事件。
  4. HTTP Streaming - 使用 Java Applets 或 ActiveX 等第三方技术将数据从服务器流式传输到客户端。

这些概念中的大多数都存在许多影响真正应用程序状态的缺点。AJAX 和 IFrames 都非常消耗资源。每次您想从服务器获取数据时,都必须创建一个对象或元素并发送 HTTP 请求。这会导致 HTTP 服务器产生大量的请求,消耗内存和处理器时间。由于头部信息来回传递,响应时间也会延迟。此外,AJAX 和 IFrames 不知道是否有可用数据;它们必须发送请求才能知道数据是否可用,这再次导致服务器和客户端之间进行了大量不必要的通信。HTTP 请求的另一个问题是它会填满您的服务器日志。对于流量大的服务器来说,这可能会令人头疼。极大的日志文件会占用处理器时间、硬盘空间和备份空间。此外,AJAX/IFrames 只能接收文本或 XML。这可能非常令人恼火,因为要在页面上使用它,您必须将其转换为可用格式。许多网站在后端使用 XML 对象,在前端使用 JSON 对象;这样,它们就必须有一个转换过程。这又会增加内存和处理器的开销。

Screenshot - ajaxprocessing.gif

许多开发人员已经注意到了 AJAX 和 IFrames 中的这些缺陷,并开始发明新技术。其中之一是 Comet,又一个听起来很干净的名字。Comet 非常难以实现,并且存在许多未知的错误。基本上,为了使用它,您通常需要创建一个自定义 Web 服务器,因为当今大多数服务器并非设计为将请求保持在打开状态。大多数服务器都内置了超时和缓存,以便以“取了就走”的方式提高效率。Comet 是比 AJAX 更进一步的改进,但它不是万能药。Comet 只能接收异步事件,它无法与服务器通信。您需要实现 Comet 来接收,并使用 AJAX 发送,这使得 Comet 依赖于 AJAX。使用 Comet 的另一个缺点是浏览器也无法很好地处理永不结束的网页。您是否曾经遇到过一个网页锁死,导致您所有其他浏览器窗口都关闭?这是由 EXE 共享内存和线程空间引起的。所以,Comet 是一个很棒的想法,但由于现有技术的缺点,它并不可行。

Screenshot - cometprocessing.gif

另一项技术是 HTTP Streaming。HTTP Streaming 有许多不同的版本。一种方法是设置一个服务器来与 ActiveX 或 Java Applet 进行双向通信。非常难以实现和维护,并且在客户端和服务器端都存在许多安全和许可问题。大多数用户不会安装您的代码,因为他们担心损坏的代码无法删除,并且可能损坏他们的操作系统或文件。这对最终用户来说可能非常可怕。

在下一节中,我将描述 FreeDOM 以及它将如何解决这些问题。

FreeDOM!

那么,一个人该如何修复这些旧技术以将网站和应用程序融为一体呢?我们是应该等待大公司创建更好的浏览器,还是应该利用现有技术立即发明它?作为一名开发者,我觉得我应该等待……但作为一名发明者,我决定让它成为现实。我想要的是自由,能够随意地在网页上做我想做的事情,构建我想构建的东西。我因此想到了 FreeDOM 这个名字,而且这确实是我们想要做的。我们希望能够自由地操作浏览器 DOM,并随时选择更新它。所以,我问自己,我可以使用哪些现有技术来创建一个真正的事件状态应用程序?一个让我想到的是 Flash,随着 AIR 和 Flex 应用程序周围的新炒作。自从 Adobe 从 Macromedia 收购 Flash 以来,Flash 在全球范围内的使用量大幅增加,很难不注意到这一点。一些最新的统计数据显示,Flash 在全球 90% 或更多的机器上安装,覆盖各种操作系统和浏览器。这让我开始思考!我能否以某种方式使用 Flash 和我现有的网页技术?Flash 在创建横幅和动画方面似乎效果很好,但它能否处理数据的来回传递?在对 ActionScript 3.0 进行了一些测试后,我找到了一种替换 AJAX、IFrames、Comet 和 HTTP Streaming 的方法。但我确信您也可以以同样的方式使用 Silverlight,我目前正在研究它,也许一些微软的同事可以帮我弄清楚。

FreeDOM 如何工作?

FreeDOM 通过使用 Flash 二进制套接字与您的自定义套接字服务器通信。要将二进制数据从 Flash 套接字传递到您的 JavaScript 网页,您需要一个名为 ExternalInterface 的自定义类。此类允许您调用 Flash 方法并为您的 JavaScript 方法添加回调。所以,让我们看一些代码,以便您能够理解。

Screenshot - FreeDOMProcessing.gif

Flash SWF 代码

将以下代码添加到文档类 .AS 文件中。它是一个自定义 JSocket 类和主影片剪辑的包装器。请记住要保持它们的大小较小。通过保持它们较小,您可以在第一个预加载器帧中加载所有内容,并绕过激活 SWF 消息。

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);
   }
  }
 }
}

接下来是包装 Flash 套接字类的 JSocket 类。下面是其代码:

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));
  }
 }
}

您可以对这些类进行很多调整,以根据您的需求进行自定义。这些只是我用于测试应用程序的简单示例。将 Flash 保持为简单的字节路由器是最佳解决方案。

JavaScript 实现

让我们看看如何在 JavaScript 中使用这些类。下面的代码是 FreeDOM 聊天程序示例。它模拟了一个自定义的个人 IRC。如果页面不可用,那是因为我正在更新它。这是该示例的代码。请记住,这是一个简单的测试,它不支持验证或错误处理,请自行承担风险。

<!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);
">
">

套接字服务器实现

我不会详细介绍代码,但它是一个用 C# 创建的简单异步套接字服务器。它处理传入的连接请求并发送响应。需要关注的重要事项是它发送的沙盒跨域策略。基本上,Flash 会向您的服务器请求此策略,以确保它可以使用您的服务器。代码如下:

/// <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
}

完整的代码可以在源代码文件中找到。您可以根据需要使用任何语言创建套接字服务器,只要它能处理请求即可。您甚至可以选择使用 HTTP Web 服务器。这完全取决于您想做什么。

结论

好了,我的 FreeDOM 教程到此结束。请回来查看更新,因为我将使用此技术创建更多应用程序。玩得开心!享受!

历史

  • 2007 年 12 月 15 日 - 创建了 FWS - FreeDOM Web Server 版本 1.0b。
  • 2007 年 12 月 13 日 - 创建了一个自定义的开源 Unix Web 服务器(用 C 编写)来处理 FreeDOM 项目。
  • 2007 年 11 月 15 日 - 购买了 FreeDOM 的域名:WWW.FDOM.US
  • 2007 年 11 月 07 日 - 创建了 ActionScript 教程。
  • 2007 年 11 月 05 日 - 为 FreeDOM 创建了 SourceForge 项目。
  • 2007 年 11 月 02 日 - 添加了 JavaScript Sockets 源代码,并为 FreeDOM (programming) 添加了 Wikipedia 条目。
  • 2007 年 11 月 01 日 - 添加了 FreeDOM Ping 示例和源代码。
  • 2007 年 10 月 30 日 - 版本 1.0A。

外部链接

使用、复制、分发和修改的条款和条件

本代码由版权持有者和贡献者“按原样”提供,并且不提供任何明示或暗示的保证,包括但不限于对适销性和特定用途适用性的暗示保证。在任何情况下,版权所有者或贡献者均不对任何直接、间接、附带、特殊、惩戒性或后果性损害(包括但不限于购买替代商品或服务;使用、数据或利润损失;或业务中断)承担任何责任,无论其如何引起,以及在任何责任理论下,无论是合同、严格责任还是侵权(包括疏忽或其他)引起的,即使已被告知发生此类损害的可能性。

© . All rights reserved.