Nätverkskommunikation och Sockets i Silverlight

I den här artikeln ska jag visa hur du kan använda Sockets i Silverlight för att 'pusha' ner data till klienten från en server för att dynamiskt uppdatera en graf - men först lite bakgrund kring nätverksstödet i Silverlight 2:

I och med Beta1 så finns det några olika valmöjligheter om du vill låta din Silverlight-applikation kommunicera med olika tjänster, antingen genom anrop mot tjänster i den domän som din applikation härstammar ifrån - eller genom anrop mot andra domäner som tillåter s.k. cross domain calls - d.v.s. anrop över domängränserna.

Jag kommer återkomma i en senare blogartikel hur kan du åstadkomma anrop mellan olika domäner. Grundinställningen är nämligen att denna typ av kommunikation inte är tillåten för att förhindra s.k. Cross Site Scripting-attacker.

Silverligt 2 Beta1 stödjer dels kommunikation med hjälp av klassen System.Net.WebClient för enklare GET-anrop av typen hämta en sträng eller stream för bearbetning samt System.Net.HttpWebRequest och System.Net.HttpWebResponse för tillfällen när du vill göra POST-anrop och kräver mer kontroll över hur Request Headers hanteras. Karen Colby har skrivit en trivsam artikel där hon går igenom skillnaderna mellan dessa klasser - så som de fungerar i Beta1 .

I en kommande release av Silverlight 2 kommer du även att kunna göra POST-baserade anrop och manipulera Request Headers med hjälp av WebClient - skillnaden blir att du med HTTPWebRequest kommer kunna välja att starta anrop från en bakgrunds-tråd (istället att starta dem från gränssnitts-tråden).

Om du vill göra anrop mot tjänster som exponerar ett tjänstekontrakt i form av en WSDL-fil, t.ex. ASMX- eller WCF-tjänst kan du enkelt generera en proxy direkt ifrån Visual Studio 2008 genom 'Add Service Reference'. Den enda skillnaden jämfört med "traditionell" .NET-utveckling är att alla tjänsteanrop i Silverlight är asynkrona. Detta gäller alltså oavsett du använder en genererad proxy eller själv gör anropen med WebClient eller HttpWebResponse.

Så oavsett vilken av de ovanstående sätten som du kommunicerar på så gäller alltså att kommunikationen är asynkron, vilket innebär att du får registrera en eventhandler för ett event som sker när svaret kommer tillbaks från tjänsten du anropar. Detta för att inte riskera att blockera gränssnitts-tråden ifall anropet tar lång tid (vilket i värsta fall skulle kunna krascha webbläsaren).

Men som sagt - den här artikeln skulle ju handla om sockets :-)

I Silverlight 2 stöds alltså också Sockets vilket gör det möjligt att skapa en socket-server med hjälp av TcpListener och sedan låta Silverlight ansluta sig mot servern. Servern kan sedan skicka uppdaterad data till klienten i realtid (eller nja, via en asynkron eventhandler på en bakgrundstråd, men i princip så nära realtid som det går att kommunicera till Silverlight).

I ett exempel som jag visade på Developer Summit uppdaterade jag en graf i Silverlight med data som skickades från en enkel Console-applikation:

graf

När jag letade efter en bra demo för att visa socket-stödet hittade jag följande mycket intressanta exempel från Marc Holmes som visar hur man kan skapa en gränssnittslös Silverlight-applikation som uppdaterar HTML med data från en socket-koppling: Marchitecture - Real-time Data in HTML with Silverlight.

Jag utgick från Marcs socket-kod men ville ha en tydligare grafisk illustration av att data skickades till klienten så jag använde den smått fantastiska open source-komponenten 'Silverlight chart' från Visifire som ger dig tillgång till en uppsjö av mycket snygga grafer som du kan använda i din egen applikation.

Tyvärr hittade jag inget sätt att databinda mot enskilda staplar i Visifire-grafen så jag tar helt enkelt bort hela komponenten från min LayoutRoot och lägger dit den med nytt data för varje 'OnReceive'-event som sker. Du kan ladda hem Visual Studio-projektet här.

En detalj som kanske måste klargöras när det gäller koden i exemplet är följande rad i Silverlight-applikationen, i eventhandler för OnReceive:

this.Dispatcher.BeginInvoke((ReceiveCallback)delegate { UpdateChart(stockList); }, null);

Dispatcher används för att få över svaret (det data som skickas) på gränssnitts-tråden så att jag kan uppdatera gränssnittet med det nya graf-datat.

Det finns några begränsningar i Silverlight 2 Beta1 vad gäller socket-kommunikation:

  • Port-spannet som du kan kommunicera via är begränsat till port 4502 - 4532
  • Du kan bara ansluta till en server på den domän som Silverlight-applikationen kommer ifrån (detta kommer ersättas med en möjlighet att göra säkra anrop över domäner i en senare version)