即时通讯系统服务器,从头开始构建一个web即时通讯系统 - 服务器端 - policyserver(5)...

policy-file是flash

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

什么是policy-file

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

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

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">

to-ports="*"

secure="false"

/>

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

policy-file的请求过程

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

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

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

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

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

1 如果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 ("/0"

== content)

{

Send(handler,

@"

encoding=""UTF-8""?>

xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""

xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">

domain=""*"" to-ports=""*"" secure=""false""

/>

permitted-cross-domain-policies=""master-only""

/>

"

);

}

else

{

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

new AsyncCallback(ReadCallback),

state);

}

}

}

policyserver确认第一个请求,是否是"/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 ("/0"

== content)

{

Send(handler,

@"

encoding=""UTF-8""?>

xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""

xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">

domain=""*"" to-ports=""*"" secure=""false""

/>

permitted-cross-domain-policies=""master-only""

/>

"

);

}

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;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值