zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,模拟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(http:
    //localhost/);

    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

  • 相关阅读:
    【娱乐向】制作Chrome天气预报扩展程序
    WCF入门四[WCF的通信模式]
    WCF入门三[WCF宿主]
    WCF入门二[WCF的配置文件]
    WCF入门一[WCF概述]
    通过Aspose.Word和ZXING生成复杂的WORD表格
    Dapper.Extension的基本使用
    startUML常用的组合片段
    Sublime Text 2 配置及其使用
    计算机领域会议汇总
  • 原文地址:https://www.cnblogs.com/waw/p/2162670.html
Copyright © 2011-2022 走看看