Laden...

[Artikel] Silverlight - Getting started

Erstellt von tom-essen vor 15 Jahren Letzter Beitrag vor 15 Jahren 23.551 Views
tom-essen Themenstarter:in
1.818 Beiträge seit 2005
vor 15 Jahren
[Artikel] Silverlight - Getting started

Dieser Artikel bezieht sich auf den Stand vom 29.08.2008 für Silverlight Version 2, Beta 2. Ich kann nicht garantieren, dass die hier beschriebenen Funktionalitäten so auch in den finalen Versionen Gültigkeit haben, werde aber versuchen, den Artikel halbwegs aktuell zu halten.

Silverlight ist als direkte Konkurrenz-Technologie zu Adobes Flash entwickelt worden. Dabei handelt es sich um sogenannte Rich Internet Applications (RIA), welche mittels Webbrowser aufgerufen und anschließend auf dem Client ausgeführt werden, für die Browser "Firefox" und "Internet Explorer" existieren entsprechende PlugIns. Ruft man eine Silverlight-Seite ohne diese PlugIns auf, erscheint ein entsprechender Hinweis. Klickt man diesen an, wird man auf eine Download-Seite gelenkt, von welcher man sich die entsprechenden PlugIns laden und installieren kann.

Wer mit Silverlight entwickeln will, sollte direkt die Silverlight Tools Beta 2 for Visual Studio 2008 installieren.

Microsoft hat für Silverlight auch ein eigenes Portal eingerichtet, welches unter http://silverlight.net/ zu finden ist.

Fertige Silverlight-Anwendungen werden mit allen benötigten Assemblies und Ressourcen in .xap-Dateien verpackt. Dabei handelt es sich um einfache zip-Archive. Die Silverlight-Browser-PlugIns erkennen diese Dateien, entpacken sie und führen die Anwendung aus. xap-Dateien entsprechen somit den war- oder ear-Dateien in J2EE (the hole application in one file). Das Deployment von Silverlight-Anwendungen wird somit sehr stark vereinfacht.

Silverlight-Anwendungen benötigen nicht zwingend einen Webserver (außer natürlich, die Anwendungslogik erfordert dies, z.B. bei Verwendung von oder auch in ASP.NET-Controls). In solchen Fällen reicht eine einfache HTML-Seite zur Anzeige der Silverlight-Anwendung aus. Eine entsprechende Standard-Seite erzeugt Visual Studio 2008 z.B. beim compilieren automatisch und ruft diese Seite beim Projektstart auch im Standard-Browser auf.

Wenn Silverlight-Anwendungen über einen Webserver verfügbar sein sollen und es sich bei der genutzten Umgebung nicht mindestens um Microsoft Windows Server 2008 und IIS 7.0 handelt, ist es erforderlich, den zugehörigen MIME-Type für xap-Dateien (application/x-silverlight-app) zur IIS-Konfiguration hinzu zu fügen.

Zum debuggen einer Silverlight-Anwendung muss der Debugger an den entsprechenden Browser-Prozess angehängt werden (Debug/Attach process).

Networking with Silverlight

Sowohl beim Aufruf von Webseiten als auch bei der Verwendung von Sockets sind einige Dinge zu beachten. Silverlight-Anwendungen laufen in einer Sandbox. Dadurch ist es z.B. nicht möglich, beliebig im Dateisystem des Clients zu arbeiten, lediglich der Zugriff auf einen eigenen Application-Storage ist möglich. Auch der Zugriff auf entfernte Rechner (Cross domain access) ist nicht ohne weiteres möglich, in jedem Fall wird eine Policy-Datei im XML-Format benötigt.

In beiden Fällen wird dabei vor dem Zugriff auch den entfernten Rechner von diesem die o.g. Policy-Datei geladen. Bei WebServern muss diese Policy-Datei dazu im Root-Verzeichnis liegen und mit clientaccesspolicy.xml benannt werden, bei Verbindungen über Sockets wird's etwas umständlicher. Hier muss auf dem entfernten Rechner ein sog. Policy-Server laufen, welcher aber schnell selbst geschrieben ist:


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();

            // Create the Listening Socket
            m_listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);

            // Put the socket into dual mode to allow a single socket
            // to accept both IPv4 and IPv6 connections
            // Otherwise, server needs to listen on two sockets,
            // one for IPv4 and one for IPv6
            // NOTE: dual-mode sockets are supported on Vista and later
            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);
        }

        // Called when we receive a connection from a client
        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);
        }
    }
}

(Quelle: Network Security Access Restrictions in Silverlight 2).

Dieser Server wird nun vom Silverlight-PlugIn auf Port 943 mit der Nachricht

<policy-file-request/>

beehrt, welche dieser mit dem Inhalt der Policy-Datei beantworten muss. Eine beispielhafte XML-Datei könnte so aussehen:

<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="file:///" />
      </allow-from>
      <grant-to>
        <socket-resource port="4502-4506" protocol="tcp" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Erst, wenn dieser Vorgang erfolgreich war, darf die Silverlight-Anwendungen auf den entfernten Rechner zugreifen, wobei auch hier eine Restriktion existiert: Es dürfen nur Ports zwischen 4502 und 4534 verwendet werden.
Zudem wurden bei meinen Tests bisher lediglich IPEndPoints als RemoteEndPoints akzeptiert, bei DnsEndPoints wurde keine Verbindung aufgebaut, d.h. Namensauflösung funktioniert anscheinend (noch) nicht.

Nobody is perfect. I'm sad, i'm not nobody 🙁