模拟IIS向Silverlight输出策略文件

简介:

问题

最近的Silverlight开发中,由于部分需求对实时性和数据量下载速度有要求,部分WCF服务配置成了netTcpBinding,这种方式跟普通的service.svc寄宿IIS不同的是,Silverlight需要的策略文件需要放置在本机IIS的根下,也就是wwwroot文件夹下,以满足Silverlight在以TCP协议调用本机WCF服务时请求策略文件。

(注:Silverlight通过TCP协议调用WCF服务时,会以http方式请求主机的一个策略文件,地址是http://localhost/clientaccesspolicy.xml)

这其实是个不太好的选择,程序运行的所需的环境被分成了两部分,同事的机器上并未安装IIS,为了大家开发简便,不用在额外安装IIS,也为了让程序更加独立,我就想能不能写代码监控80端口模拟IIS向Silverlight输出这个策略文件。

解决方法

有了这个想法之后,首先想到的是通过Socket进行监听,因为此前在MSDN上看到过这种方式,但很无奈,将代码转移过来之后,并未成功。相信做过Silverlight在Socket方面应用的朋友对下面这个PolicyServer类很熟悉吧。

 
 
代码
using  System;
using  System.IO;
using  System.Net;
using  System.Net.Sockets;

namespace  PolicyServer
{
//  Encapsulate and manage state for a single connection from a client
class  PolicyConnection
{
    
private  Socket m_connection;

    
//  buffer to receive the request from the client
     private   byte [] m_buffer;
    
private   int  m_received;

    
//  the policy to return to the client
     private   byte [] m_policy;

    
//  the request that we're expecting from the client
     private   static   string  s_policyRequestString  =   " <policy-file-request/> " ;



    
public  PolicyConnection(Socket client,  byte [] policy)
    {
        m_connection 
=  client;
        m_policy 
=  policy;

        m_buffer 
=   new   byte [s_policyRequestString.Length];
        m_received 
=   0 ;

        
try
        {
            
//  receive the request from the client
            m_connection.BeginReceive(m_buffer,  0 , s_policyRequestString.Length, SocketFlags.None,  new  AsyncCallback(OnReceive),  null );
        }
        
catch  (SocketException)
        {
            m_connection.Close();
        }
    }

    
//  Called when we receive data from the client
     private   void  OnReceive(IAsyncResult res)
    {
        
try
        {
            m_received 
+=  m_connection.EndReceive(res);

            
//  if we haven't gotten enough for a full request yet, receive again
             if  (m_received  <  s_policyRequestString.Length)
            {
                m_connection.BeginReceive(m_buffer, m_received, s_policyRequestString.Length 
-  m_received, SocketFlags.None,  new  AsyncCallback(OnReceive),  null );
                
return ;
            }

            
//  make sure the request is valid
             string  request  =  System.Text.Encoding.UTF8.GetString(m_buffer,  0 , m_received);
            
if  (StringComparer.InvariantCultureIgnoreCase.Compare(request, s_policyRequestString)  !=   0 )
            {
                m_connection.Close();
                
return ;
            }

            
//  send the policy
            m_connection.BeginSend(m_policy,  0 , m_policy.Length, SocketFlags.None,  new  AsyncCallback(OnSend),  null );
        }
        
catch  (SocketException)
        {
            m_connection.Close();
        }
    }

    
//  called after sending the policy to the client; close the connection.
     public   void  OnSend(IAsyncResult res)
    {
        
try
        {
            m_connection.EndSend(res);
        }
        
finally
        {
            m_connection.Close();
        }
    }
}

//  Listens for connections on port 943 and dispatches requests to a PolicyConnection
class  PolicyServer
{
    
private  Socket m_listener;
    
private   byte [] m_policy;

    
//  pass in the path of an XML file containing the socket policy
     public  PolicyServer( string  policyFile)
    {
        
//  Load the policy file
        FileStream policyStream  =   new  FileStream(policyFile, FileMode.Open);

        m_policy 
=   new   byte [policyStream.Length];
        policyStream.Read(m_policy, 
0 , m_policy.Length);

        policyStream.Close();

        m_listener 
=   new  Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);

        m_listener.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)
27 0 );

        m_listener.Bind(
new  IPEndPoint(IPAddress.IPv6Any,  943 ));
        m_listener.Listen(
10 );

        m_listener.BeginAccept(
new  AsyncCallback(OnConnection),  null );
    }

     
public   void  OnConnection(IAsyncResult res)
    {
        Socket client 
=   null ;

        
try
        {
            client 
=  m_listener.EndAccept(res);
        }
        
catch  (SocketException)
        {
            
return ;
        }

        
//  handle this policy request with a PolicyConnection
        PolicyConnection pc  =   new  PolicyConnection(client, m_policy);

        
//  look for more connections
        m_listener.BeginAccept( new  AsyncCallback(OnConnection),  null );
    }

    
public   void  Close()
    {
        m_listener.Close();
    }
}
public   class  Program
{
    
static   void  Main( string [] args)
    {
        
if  (args.Length  ==   0 )
        {
            Console.WriteLine(
" usage: PolicyServer.exe PolicyFile.xml " );
            
return ;
        }

        PolicyServer ps 
=   new  PolicyServer(args[ 0 ]);
        System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
    }
}
}

 

此路不通之后,又想起使用HttpListener类,看看是否能够监听http请求,果然能够截获HTTP的请求
HttpListener listener = new HttpListener();
listener.Prefixes.Add(
listener.Start();Console.WriteLine("开始监听…");

HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
但是这种方式有个明显的缺点,就是线程是阻塞的。于是,又想到使用线程池。
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Listen));

 

private static void Listen(object state)
        {
            while (httpListener.IsListening)
            {
                httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), httpListener);
                listenForNextRequest.WaitOne();
            }
        }

这样的话,每接收一个请求便会异步处理这个请求。在请求的处理上,接收请求后需要向外输出策略文件流,供silverlight端接收验证。

using (System.Net.HttpListenerResponse response = context.Response)
            {
                System.Threading.Thread.Sleep(1000);

                string responseString = "<?xml version=\"1.0\" encoding=\"utf-8\"?> "
                                        + " <access-policy> "
                                        + "    <cross-domain-access> "
                                           + "    <policy> "
                                              + "    <allow-from http-request-headers=\"*\">"
                                                 + "    <domain uri=\"*\" /> "
                                                 + " </allow-from> "
                                                 + " <grant-to> "
                                                   + "  <socket-resource port=\"4502-4534\" protocol=\"tcp\" /> "
                                                + "  </grant-to> "
                                             + "  </policy> "
                                        + "    </cross-domain-access>"
                                        + " </access-policy>";
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
                response.ContentLength64 = buffer.LongLength;
                response.OutputStream.Write(buffer, 0, buffer.Length);
            } 

启动这个模拟服务,将clientaccesspolicy从wwwroot中移除后再运行一下程序,OK,我们不再需要将策略文件放到IIS下了。

提醒 

如果你的机器装了IIS,请还是放一个策略文件到wwwroot吧,否则就停掉IIS再使用这个类,因为IIS和这个类只能有一方监听80端口。

 

本文中的这个类参考了http://forums.silverlight.net/forums/p/113106/255614.aspx





     本文转自wengyuli 51CTO博客,原文链接:http://blog.51cto.com/wengyuli/586751,如需转载请自行联系原作者

相关文章
|
5月前
发布在IIS的apk或者ipa文件无法访问
发布在IIS的apk或者ipa文件无法访问
71 0
|
8月前
|
XML 数据格式 Windows
IIS服务启动提示当文件已存在时,无法创建该文件,183
IIS服务启动提示当文件已存在时,无法创建该文件,183
234 0
|
9月前
|
开发框架 .NET
解决NET Core发布iis项目覆盖原有的项目时"另一个程序正在使用此文件,进程无法访问"
解决NET Core发布iis项目覆盖原有的项目时"另一个程序正在使用此文件,进程无法访问"
|
开发工具 git
使用批处理(.bat)文件一键编译 .NET CORE 网站并发布至 IIS
向 IIS 发布网站的传统方式是:开发人员在开发机上对项目源代码编译并将生成的程序集复制到服务器上的网站目录中.在 .NET CORE 时代开启后,复制操作很容易因为文件占用而失败.
227 0
|
网络协议 文件存储 数据安全/隐私保护
通过安装和配置AD域解决Windows Server 2016的IIS无法加载SMB文件卷文件的问题
通过客户反馈我们发现Windows Server 2016的IIS无法加载SMB文件卷的文件,显示的错误是:未能加载文件或程序集。经过我们不断研究和实验,发现在安装和配置好AD域之后,2016上IIS加载文件卷文件发生错误的问题可以得到解决。
4118 0
通过安装和配置AD域解决Windows Server 2016的IIS无法加载SMB文件卷文件的问题
skyline5:三维skyline fly文件发布网络包并在iis发布
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bitree1/article/details/79240782 1.发布网络包并发布 (1)选择“发布”中的“发布工程为网络包”,如下图所示: (2)在弹出的对话框中输入相应的内容,并点击“确定”,如下图所示:   其中:打包路径:将文件打包到指定的目录中;目标URL:将来要访问的URL路径; (3)点击确定后,将弹出对话框,这时点击“是”,创建文件夹。
1060 0