设为首页 收藏本站
查看: 995|回复: 0

[经验分享] 模拟IIS向Silverlight输出策略文件

[复制链接]

尚未签到

发表于 2015-8-12 10:36:06 | 显示全部楼层 |阅读模式
问题
  最近的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类很熟悉吧。



DSC0000.gif DSC0001.gif 代码

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(http://localhost/);listener.Start();Console.WriteLine("开始监听&#8230;");
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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-97831-1-1.html 上篇帖子: VS中使用本地IIS调试ASP.NET项目 下篇帖子: IIS服务器应用程序不可用的解决办法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表