代码改变世界

即时通 系统 - 客 端 - lash ocket

2010-02-27 09:38  宝宝合凤凰  阅读(4015)  评论(1编辑  收藏  举报

简介

本教程将描述从头开始构建一个web即时通讯系统的几个主要过程,以及它们是如何结合在一起的。

在这篇教程中,将学习flashsocket、policyserver、javascript和flash的互操作、swf的多语言编译器haxe、sessionserver的三种模式以及各自的优点与缺点。

本教程会在完成基础的web im通讯系统以后,深入的讨论如何构建高性能的和分布式的通讯系统。

目录(有待更新)

概述

基础

客户端-flash

客户端-javascript

服务器端-policyserver

服务器端-sessionserver-select模式

服务器端-sessionserver-多线程模式

服务器端-sessionserver-异步模式

...

预备知识

本教程的服务器端使用c# 2.0编码,客户端使用flash来完成通讯,并使用js来描述浏览器端行为。

本教程假定您已经熟悉socket/tcp的通讯原理,或者可以参考http://简介%20本教程将描述从头开始构建一个web即时通讯系统的几个主要过程,以及它们是如何结合在一起的。%20在这篇教程中,将学习flashsocket、policyserver、javascript和flash的互操作、swf的多语言编译器haxe、sessionserver的三种模式以及各自的优点与缺点。%20本教程会在完成基础的web%20im通讯系统以后,深入的讨论如何构建高性能的和分布式的通讯系统。%20%20目录(有待更新)%20概述%20基础%20客户端-flash%20客户端-javascript%20服务器端-policyserver%20服务器端-sessionserver-select模式%20服务器端-sessionserver-多线程模式%20服务器端-sessionserver-异步模式%20...%20%20预备知识%20本教程的服务器端使用c/# 2.0编码,客户端使用flash来完成通讯,并使用js来描述浏览器端行为。 本教程假定您已经熟悉socket/tcp的通讯原理,或者可以参考http://en.wikipedia.org/wiki/Transmission_Control_Protocol

 
========

页面刷新

页面刷新即通过刷新页面来更新聊天记录,通过页面提交来发送消息。毫无疑问,这种实现是最简单的,也是效率最差的。它意味着每一次收发消息都对应了一次httprequest,而http作为应用层协议,连接代价是相当大的。

ajax

ajax可以理解为xmlhttprequest,本质与页面刷新相同,但是页面上会有很多资源文件,比如图片、css、js、flash,这些文件会随着页面刷新并且没有做浏览器端缓存的时候更新。为了接收或者发送数十字节的消息而更新可能多达数百K字节的字节文件,显然是很愚蠢的。所以使用ajax来通讯,"只"需要为消息的获取或者发送而进行请求。ajax从这一点上是优于页面刷新的。

comet/server push

ajax为每一个消息的发送和接收建立一个新的http连接,并在短暂通讯完毕以后关闭。而server push的本质是http长连接,它的思路是,让服务器和浏览器维持着一个时间跨度很长的http连接,这个http连接可能大部分时候并不需要做什么,只在有消息的时候,向浏览器写入。所以这种方式避免了建立以及关闭http连接时大量的开销,提高了资源利用率。不过server push也有它的问题,在一个web服务器上,http连接数并非没有限制,所以如果把http连接数也看作是系统资源的话,server push确实是对于此资源的极大浪费了。

comet是server push的思路上的一种改进,它在ajax和server push之间使用了一种折中的策略,在高频率的通讯过程中,comet使用http长连接。一旦通讯变得不频繁,comet便释放http连接,并在下一次通讯的时候,是情况而定,看是否需要建立http长连接。

server push在asp.net中可以通过下面的代码片段实现:

        protected void Page_Load(object sender, EventArgs e)

        {

            while (IsSessionEnd as bool)

            {

                lock (token as object)

                {

                    Response.Write(message as string);

                    Response.Flush();

                }

            }

        }

(当然,在实际编码中,as type的写法是不必要的,这里只是为了说明类型)

可以看到,在上面的代码片段中,会话结束前,Response.End()都不会被调用。

socket/tcp

在页面刷新、ajax和server push的思路里,最重要的一个共同点就是基于http。http是web的基础,然而http并不符合即时通讯系统的思想,很大程度上是由于http几个特点:

[

  http是无状态的,

  http是一问一答的,

  http是单播的,

  http是应用层协议,通讯代价很大

]

然而http的下层协议tcp/udp就很适合用来做为即时通讯系统的通讯协议。可是在web标准里,一切都是基于http的。幸运的是,我们可以通过浏览器插件的信路来完成通讯,在java applet、flash和silverlight里,都提供了socket/tcp,并且dom和flash或silverlight是可以互操作的。这就意味着,dom也可以间接的使用socket来与服务器通讯。

============

摘要

在上一篇"基础-web即时通讯系统的四种实现"中,我们讨论了socket/tcp相对于其他基于http的通讯方式的优越。本文则详细描述如何使用flash/actionscript,来实现socket/tcp,并使用开源的haxe来编译它。

flash中的socket

socket的构造

实例化一个socket对象,只需要一行代码:

static var socket = new flash.net.Socket();

主动方法和被动方法

而剩下的工作便是把这个对象包装成合适接口供javascript调用了。这时候主要有两类方法,一类是主动性方法,由客户端直接发起的便是主动方法,比如connect、send就是主动方法。

另一类是被动方法,也就是服务器端发起,然后在客户端触发事件,并被调用的回调方法,比如connected、receive就是被动方法。

主动方法和被动方法直接决定了代码的结构,决定了javascript和flash的互操作方式。

事件时序

我们从事件时序上来看一下flash中socket的可能的一个过程:

flash加载完毕

我们先注册一个回调,在flash加载完毕的时候,获得一个通知,以便为下面的连接做一些设置和准备。

flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");

建立一个socket连接

接着我们便使用下面的方法来建立一个socket连接

static function connect(host : String, port : String) {

trace("Connecting to socket server at " + host + ":" + port);

socket.connect(host, Std.parseInt(port)); 

}

这里的两个形参:host为ip或者域名,port为通讯的端口。trace为输入到flash控制台的方法。

flash socket在端口上是有限制的,仅被允许访问4502-4534这个网段里的端口。

flash socket在执行socket.connect(host, Std.parseInt(port))时,无论如何,会先访问843端口,如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接,并与调用方法时指定的端口连接。

如果flash没有正常得到这个policy-file.xml的话,从调用方法时指定的端口连接时刚连接上便获得这个policy-file.xml也是可以的。如果这两种情况下都没有获得policy-file.xml,那么通讯是不能继续的。这是flash特有的安全机制。

关于这个安全机制,以及提供这个policy-file.xml服务器程序policyserver,我会在稍后的教程里详细介绍。

连接失败

如果flash没有顺利连接上服务器,或者超时,我们可以注册一个回调来获得连接失败的通知,已准备重连。

socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e) : Void {

trace("SECURITY ERROR : " +  e.text);

flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" +e.text+ "')", 0);

}

);

连接成功

如果顺利连接上了,这时候,我们可以通过下面的代码来注册一个回调,得到连接状态更新的通知。

socket.addEventListener(flash.events.Event.CONNECT, function(e) : Void { 

trace("Connected to server");

flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);

}

);

发送消息

下面我们便可以从flash发送消息给服务器了

static function send(msg) {

if (socket.connected) {

trace("Writing '" + msg + "' to server");

//socket.writeMultiByte(msg,"gb2312");

socket.writeUTFBytes(msg);

socket.flush();

} else {

trace("Cannot write to server because there is no connection!");

}

我们可以看到,这段代码里,我注释掉了socket.writeMultiByte(msg,"gb2312");此句代码。从字面意思上也可以了解,这里是使用了"gb2312"的编码来发送消息。

flash socket提供了丰富的方法,在以后的教程里,我会详细描述flash.net.Socket的每一个方法。

获取消息

如果服务器发送消息给客户端,我们也可注册一个回调来获取消息的内容

socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e) : Void {

//var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");

var msg = socket.readUTFBytes(socket.bytesAvailable);

trace("Received : " + msg );

flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);

}

);

这里和发送消息类似,我们可以使用不同的编码标准来解码我们获得的字节流。

关闭连接

在完成通讯以后,如果我们需要关闭这个socket连接的话,可以使用下面的主动方法。

static function close() {

if (socket.connected) {

    trace("Closing current connection");

socket.close();

} else {

trace("Cannot disconnect to server because there is no connection!");

}

}

连接断开

在socket连接断开以后,我们可以注册一个回调来获得通知

socket.addEventListener(flash.events.Event.CLOSE, function(e) : Void {

trace("Disconnected from server");

flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);

}

);

IO错误

在通信过程中,如果有错误发生的话,我们可以这样来获得通知

socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) : Void {

trace("IOERROR : " +  e.text);

flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')" ,0);

}

);

完整的代码

//SocketBridge.hx

class SocketBridge {

 

         static var socket = new flash.net.Socket();

        

         static var jsScope;

 

             static function main() {

                  

                   if (flash.external.ExternalInterface.available) {

                      

                   jsScope = flash.Lib.current.loaderInfo.parameters.scope;

                  

                   if (jsScope == null) {

                            jsScope = "";

                   } else {

                            jsScope += ".";

                   }

 

                   /* Calls the javascript load method once the SWF has loaded */

                   flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");                    

                  

                   // Set event listeners for socket

                  

                   // CONNECT

                   socket.addEventListener(flash.events.Event.CONNECT, function(e) : Void {

                                     trace("Connected to server");

                                     flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);

                            }

                   );

 

                   // CLOSE

                   socket.addEventListener(flash.events.Event.CLOSE, function(e) : Void {

                                     trace("Disconnected from server");

                                     flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);                               

                            }

                   );

                           

 

                   // IO ERROR

                   socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) : Void {

                                     trace("IOERROR : " +  e.text);

                                     flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')" ,0);

                            }

                   );

 

                   // SECURITY ERROR

                   socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e) : Void {

                                     trace("SECURITY ERROR : " +  e.text);

                                     flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" +e.text+ "')", 0);

                            }

                   );

 

                   // SOCKET DATA

                   socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e) : Void {

                                               //var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");

                                               var msg = socket.readUTFBytes(socket.bytesAvailable);

                                               trace("Received : " + msg );

                                               flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);

                                     }

                            );

 

                   /* Set External Interface Callbacks */

                  

                   // connect(host, port)

                   flash.external.ExternalInterface.addCallback("connect", connect);                  

                  

                   // disconnect()

                   flash.external.ExternalInterface.addCallback("close", close);                                               

                  

                   // send()

                   flash.external.ExternalInterface.addCallback("send", send);           

                  

                   // log()

                   flash.external.ExternalInterface.addCallback("log", log);

 

             } else {

                   trace("Flash external interface not available");

             }  

 

    }

   

    /**

     * Connect to new socket server

     * @param host The host the socket server resides on

     * @param port The socket servers port

     */

    static function connect(host : String, port : String) {

             trace("Connecting to socket server at " + host + ":" + port);

                   socket.connect(host, Std.parseInt(port));          

    }

   

    /**

     * Disconnect the socket

     */

    static function close() {

             if (socket.connected) {

                  trace("Closing current connection");

                            socket.close();

                   } else {

                            trace("Cannot disconnect to server because there is no connection!");

                   }

    }

 

    /**

     * Write string to the socket

     */

    static function send(msg) {

             if (socket.connected) {

                      trace("Writing '" + msg + "' to server");

                            //socket.writeMultiByte(msg,"gb2312");

                            socket.writeUTFBytes(msg);

                            socket.flush();

                   } else {

                            trace("Cannot write to server because there is no connection!");                 

                   }

    }   

 

    /**

     * Log

     */

    static function log(msg) {

             trace("log : "+msg);

    }  

 

}

编译

我使用了开源的haxe来编译,你也可以通过http://haxe.org/file/hxinst-win.exe获取它。

编写完SocketBridge.hx以后,你需要在同一个目录下建一个compile.hxml文本文件,它的内容是:

-swf socket_bridge.swf

-swf-version 9

-main SocketBridge

在安装过haxe的情况下,只要你双击执行compile.hxml,便可以在同一目录下获得编译出的socket_bridge.swf 。

在下一篇教程"客户端-javascript和flash互操作"里,我们将了解如何使用socket_bridge.swf来操作javascript进行socket/tcp通讯。

=============================

在上一篇"基础-web即时通讯系统的四种实现"中,我们讨论了如何使用flash/actionscript,来实现socket/tcp,并使用开源的haxe来编译。而本文将介绍如何使用javascript调用编译出的flash来进行通讯。

加载flash


我使用swfobject.js来加载我们编译出的flash文件——socket_bridge.swf。


swfobject.js是一种易用的、符合标准的在网页中嵌入flash对象的方法,你只需要在网页中引用一个小巧的js文件。你在网上可以很容易下载到它。

初始化flash


我们可以调用swfobject.js里提供的方法,来编写跨浏览器的加载flash的代码。


function initialFlash(){
    var so = new SWFObject("socket_bridge.swf", "socketBridge", "800", "300", "9", "#ffffff");  
    so.addParam("allowscriptaccess", "always");  
    so.addVariable("scope", "socket");  
    so.write("flashcontainer");
}


这段代码的意义是,将socket_bridge.swf下载到浏览器端,并在id为flashcontainer的html元素下。并将对象名为socket的对象暴露给flash。


考虑到这里使用到了flashcontainer这个html元素,我们需要在这个html元素已经存在于dom以后才可以执行这段代码,比如在window.onload事件里。


onload=function(){
    initialFlash();
}

封装flash socket


我们需要在initialFlash执行前声明一个对象名为socket的对象。


var socket = {
 config :{
  ip:"127.0.0.1",
  port:4502,
  flashcontainer:"flashcontainer",
  auto:true
 },

 connect : function() {
         socket.flash.log("begin connect to session server");            
         socket.flash.connect(socket.config.ip, socket.config.port);
 },
 
 send : function(msg) {
  if(socket.isConnected >= 1){
   socket.flash.send(msg);
  }
 },
 
 loaded : function() {        
  socket.flash = document.getElementById("socketBridge");
  socket.isConnected = 0;
  if(socket.config.auto){
      socket.connect();
  }
 },
 
 connected : function() {
     socket.isConnected = 1;
     socket.flash.log("connected to session server");
 },

 close : function() {
  socket.flash.close();
  socket.isConnected = 0;

  socket.flash.log("close connection");

 },   
 
 disconnected : function() {
  socket.isConnected = 0;
  socket.flash.log("disconnected");
 },  
   
 ioError: function(msg) {
  socket.flash.log(msg);
        socket.isConnected = 0;

 },
 
 securityError: function(msg) {
  socket.flash.log(msg);
        socket.isConnected = 0;

 },  
 
 receive: function(msg) {
     //callback
 }
};


这里有几处需要注意:


socket.config.auto为true时,flash文件socket_bridge.swf加载完毕时,自动调用socket.flash.connect连接本地的4502端口。


socket.flash.log为对flash的trace函数的封装,可以调用它向flash控制台写入一些调试信息。


socket.isConnected为socket连接的状态量,实际上在真实的聊天室里,连接完以后还需要登录和验证的一系列过程,所以用此变量来表示这些自定义状态。

操作flash socket


有了这个socket对象,我们便可以在javascript里操作flash socket了。比如说:


socket.send("hello socket")

效果演示

 

完整代码

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

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <script src="js/swfobject.js"></script>

    <script>

var socket = {

    config :{

        ip:"127.0.0.1",

        port:4502,

        flashcontainer:"flashcontainer",

        auto:true

    },

 

     connect : function() {

        socket.flash.log("begin connect to session server");           

        socket.flash.connect(socket.config.ip, socket.config.port);

     },

    

     send : function(msg) {

         if(socket.isConnected >= 1){

              socket.flash.send(msg);

         }

     },

    

     loaded : function() {       

         socket.flash = document.getElementById("socketBridge");

         socket.isConnected = 0;

         if(socket.config.auto){

             socket.connect();

         }

     },

    

     connected : function() {

         socket.isConnected = 1;

         socket.flash.log("connected to session server");

     },

 

     close : function() {

         socket.flash.close();

        socket.isConnected = 0;

        socket.flash.log("close connection");

     },           

    

     disconnected : function() {

         socket.isConnected = 0;

         socket.flash.log("disconnected");

     },      

             

     ioError: function(msg) {

         socket.flash.log(msg);

        socket.isConnected = 0;

     },                         

    

     securityError: function(msg) {

         socket.flash.log(msg);

        socket.isConnected = 0;

     },      

                                

     receive: function(msg) {

         //callback

     }

};

function initialFlash(){

    var so = new SWFObject("socket_bridge.swf""socketBridge""800""300""9""#ffffff"); 

    so.addParam("allowscriptaccess""always"); 

    so.addVariable("scope""socket"); 

    so.write(socket.config.flashcontainer);

}

onload=function(){

    initialFlash();

}

    </script>

   

 

   

 

</head>

<body>

    <textarea id="cmd" style="width400pxheight300px;">socket.send("")</textarea>

    <input type="button" value="exec" onclick="eval(document.getElementById('cmd').value);" />

    <div id="flashcontainer">

    </div>

</body>

</html>

0
0
(请您对文章做出评价)

 

===============

 

简介

policy-fileflash socket安全机制的重要部分,而本文详细介绍提供policy-filepolicyserver的实现过程。事实上,policyserver本身也是一个socket服务器端的简单原型。了解本文也将为教程中后续的sessionserver的讨论有所铺垫。

 

什么是policy-file

policy-file是一个flash的安全控制机制,它的设置决定了是否允许flash程序进行跨域通讯。这是对ajaxdom无法跨域的一个拓展。

一个典型的policy-file可能是这样的:

<?xml version="1.0encoding="UTF-8"?>

<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instancexsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">

  <allow-access-from domain="*to-ports="*secure="false" />

  <site-control permitted-cross-domain-policies="master-only" />

</cross-domain-policy>

其中你可以在policy-file指定允许开放的域名/ip,或者是端口。

 

policy-file的请求过程

1 flash socket在真正执行socket.connect,以连接到指定的ip和端口前,会先尝试连接该ip843端口

2如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接

3 flash会分析policy-file的内容,并确认是否访问指定的ip和端口是否是被允许的

如果指定的ip和端口是否是被允许的,flash才会真正的连接到指定的ip和端口

 

policy-file的请求过程的异常情况

如果843端口连接失败或者超时,flash依然会连接指定端口,并尝试从指定端口获取policy-file

2如果policy-file获取失败,或者指定的ip和端口不在policy-file允许范围内,flash socket将抛出flash.events.SecurityErrorEvent.SECURITY_ERROR这个异常

 

policyserver的实现

监听端口

    IPAddress ipAddress = IPAddress.Parse(ip);

    IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 843);

 

    Socket listener = new Socket(AddressFamily.InterNetwork,

        SocketType.Stream, ProtocolType.Tcp);

 

    try

    {

        listener.Bind(localEndPoint);

        listener.Listen(100);

 

        while (true)

        {

            allDone.Reset();

 

            listener.BeginAccept(

                new AsyncCallback(AcceptCallback),

                listener);

 

            allDone.WaitOne();

        }

 

    }

    catch (Exception e)

    {

        Console.WriteLine(e.ToString());

    }

其中allDone是一个信号控制变量。在接收到一个新的连接请求前,policyserver会阻塞在allDone.WaitOne()这一句。

 

处理接入请求

    public static void AcceptCallback(IAsyncResult ar)

    {

        allDone.Set();

 

        Socket listener = (Socket)ar.AsyncState;

        Socket handler = listener.EndAccept(ar);

 

        Console.WriteLine("conneceted : " + handler.RemoteEndPoint.ToString());

        StateObject state = new StateObject();

        state.workSocket = handler;

        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,

            new AsyncCallback(ReadCallback), state);

}

 

在上面的代码里,我还把客户端的ip写入控制台,并将异步接受flash的第一个请求。

 

读入请求,写回policy-file

当接入一个连接请求以后,allDone.Set()释放信号量,在等待接入的循环体里,将完成一次过程,进入下一次循环。

    public static void ReadCallback(IAsyncResult ar)

    {

        String content = String.Empty;

 

        StateObject state = (StateObject)ar.AsyncState;

        Socket handler = state.workSocket;

 

        int bytesRead = handler.EndReceive(ar);

 

        if (bytesRead > 0)

        {

            state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));

 

            content = state.sb.ToString();

            if ("<policy-file-request/>\0" == content)

            {

                Send(handler,

                    @"

<?xml version=""1.0"" encoding=""UTF-8""?>

<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">

<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />

<site-control permitted-cross-domain-policies=""master-only"" />

</cross-domain-policy>

"

                    );

            }

            else

            {

                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,

                new AsyncCallback(ReadCallback), state);

            }

        }

    }

policyserver确认第一个请求,是否是"<policy-file-request/>\0",如果是,则将策略文件异步写回给flash

 

关闭socket

    private static void SendCallback(IAsyncResult ar)

    {

        try

        {

            Socket handler = (Socket)ar.AsyncState;

 

            int bytesSent = handler.EndSend(ar);

            Console.WriteLine("policy file sent");

 

            handler.Shutdown(SocketShutdown.Both);

            handler.Close();

 

        }

        catch (Exception e)

        {

            Console.WriteLine(e.ToString());

        }

    }

写完策略文件后,在控制台写入作为提示信息。并关闭socket

 

配置

考虑到这个policyserver可能部署在不同的环境,为了方便部署,我从文件名中读取配置。

    Match m = Regex.Match(Environment.CommandLine, @"\d+\.\d+\.\d+\.\d+");

    string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;

    Console.WriteLine("binding to " + ip);

比如编译结果文件为SimplePolicyServer.exe,我把它文件名修改为SimplePolicyServer.192.168.1.10.exe ,那么它在执行的时候,便会监听192.168.1.10这个ip的端口。如果没有指定正确的ip的话,默认绑定到本机,即127.0.0.1

 

完整代码

using System;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Net.NetworkInformation;

using System.Text.RegularExpressions;

 

public class StateObject

{

    public Socket workSocket = null;

    public const int BufferSize = 1024;

    public byte[] buffer = new byte[BufferSize];

    public StringBuilder sb = new StringBuilder();

}

 

namespace SimplePolicyServer

{

    class Program

    {

        // Thread signal.

        public static ManualResetEvent allDone = new ManualResetEvent(false);

 

        public static void StartListening()

        {

            byte[] bytes = new Byte[1024];

            Match m = Regex.Match(Environment.CommandLine, @"\d+\.\d+\.\d+\.\d+");

            string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;

            Console.WriteLine("binding to " + ip);

            IPAddress ipAddress = IPAddress.Parse(ip);

            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 843);

 

            Socket listener = new Socket(AddressFamily.InterNetwork,

                SocketType.Stream, ProtocolType.Tcp);

 

            try

            {

                listener.Bind(localEndPoint);

                listener.Listen(100);

 

                while (true)

                {

                    allDone.Reset();

 

                    listener.BeginAccept(

                        new AsyncCallback(AcceptCallback),

                        listener);

 

                    allDone.WaitOne();

                }

 

            }

            catch (Exception e)

            {

                Console.WriteLine(e.ToString());

            }

 

            Console.Read();

 

        }

 

        public static void AcceptCallback(IAsyncResult ar)

        {

            allDone.Set();

 

            Socket listener = (Socket)ar.AsyncState;

            Socket handler = listener.EndAccept(ar);

 

            Console.WriteLine("conneceted : " + handler.RemoteEndPoint.ToString());

            StateObject state = new StateObject();

            state.workSocket = handler;

            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,

                new AsyncCallback(ReadCallback), state);

        }

 

        public static void ReadCallback(IAsyncResult ar)

        {

            String content = String.Empty;

 

            StateObject state = (StateObject)ar.AsyncState;

            Socket handler = state.workSocket;

 

            int bytesRead = handler.EndReceive(ar);

 

            if (bytesRead > 0)

            {

                state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));

 

                content = state.sb.ToString();

                if ("<policy-file-request/>\0" == content)

                {

                    Send(handler,

                        @"

<?xml version=""1.0"" encoding=""UTF-8""?>

<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">

<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />

<site-control permitted-cross-domain-policies=""master-only"" />

</cross-domain-policy>

"

                        );

                }

                else

                {

                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,

                    new AsyncCallback(ReadCallback), state);

                }

            }

        }

 

        private static void Send(Socket handler, String data)

        {

            byte[] byteData = Encoding.ASCII.GetBytes(data);

 

            handler.BeginSend(byteData, 0, byteData.Length, 0,

                new AsyncCallback(SendCallback), handler);

        }

 

        private static void SendCallback(IAsyncResult ar)

        {

            try

            {

                Socket handler = (Socket)ar.AsyncState;

 

                int bytesSent = handler.EndSend(ar);

                Console.WriteLine("policy file sent");

 

                handler.Shutdown(SocketShutdown.Both);

                handler.Close();

 

            }

            catch (Exception e)

            {

                Console.WriteLine(e.ToString());

            }

        }

 

 

        public static int Main(String[] args)

        {

            StartListening();

 

            return 0;

        }

    }

}