Transcript

Charles Petzoldwww.charlespetzold.com

Networking

Agenda

• WebClient• HttpWebRequest• Consuming REST services• Consuming ASMX and WCF services• Push notification services• Tile scheduling

Silverlight vs. SWP

Feature Silverlight SWPCross-domain networking

Requires elevated permissions or policy file No restrictions

Cross-protocol networking

Requires elevated permissions or policy file No restrictions

Networking stacks

Browser stack and client stack Client stack only

Duplex networking

PollingDuplexHttpBinding and NetTcpBinding HttpNotificationChannel

Authentication Basic, digest, NTLM, and forms Basic and forms

Sockets Yes Not supported

UDP multicasting Yes Not supported

• Event-based HTTP networking API– Asynchronous operation only

• Commonly used to download assets– DownloadStringAsync - String– OpenReadAsync – Stream (binary)– Also features uploading methods

• Fires progress and completion events and supports cancellation of pending requests– Event handlers execute on calling thread

WebClient

Downloading an Image File

WebClient wc = new WebClient();wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OnOpenReadCompleted);wc.OpenReadAsync(new Uri ("http://www.wintellect.com/images/Bandit.jpg")); ...private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs e){ if (e.Error == null) { BitmapImage bi = new BitmapImage(); bi.SetSource(e.Result); MyImage.Source = bi; }}

Downloading a Zipped Image FileWebClient wc = new WebClient();wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OnOpenReadCompleted);wc.OpenReadAsync(new Uri("http://www.wintellect.com/images/Images.zip")); ...private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs e){ if (e.Error == null) { StreamResourceInfo sri1 = new StreamResourceInfo(e.Result, null); StreamResourceInfo sri2 = Application.GetResourceStream(sri1, new Uri("Bandit.jpg", UriKind.Relative)); BitmapImage bi = new BitmapImage(); bi.SetSource(sri2.Stream); MyImage.Source = bi; }}

Updating a Progress Bar

WebClient wc = new WebClient();wc.DownloadProgressChanged += OnDownloadProgressChanged; ...private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e){ Progress.Value = e.ProgressPercentage;}

Downloading an RSS Feed

WebClient wc = new WebClient();wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OnOpenReadCompleted);wc.OpenReadAsync(new Uri("...")); ...private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs e){ if (e.Error == null) { using (StreamReader reader = new StreamReader(e.Result)) { string rss = reader.ReadToEnd(); // TODO: Parse the feed } }}

demoWebClient

• Delegate-based HTTP networking API– Asynchronous operation only

• Generally used to call untyped HTTP services– e.g., REST services

• Completion methods called on background threads from CLR thread pool– Use Dispatcher.BeginInvoke to update UI

HttpWebRequest

Calling a REST Service (GET)

HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create (new Uri("http://contoso.com/weather/98052"));request.BeginGetResponse (new AsyncCallback(OnGetResponseCompleted), request); ...private void OnGetResponseCompleted(IAsyncResult ar){ HttpWebRequest request = (HttpWebRequest)ar.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar);

using (StreamReader reader = new StreamReader(response.GetResponseStream())) { string result = reader.ReadToEnd(); ... }}

Calling a REST Service (POST)

HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create (new Uri("http://contoso.com/weather"));request.Method = "POST";request.ContentType = "application/x-www-form-urlencoded";request.BeginGetRequestStream(new AsyncCallback (OnGetRequestStreamCompleted), request); ...private void OnGetRequestStreamCompleted(IAsyncResult ar){ HttpWebRequest request = (HttpWebRequest)ar.AsyncState; using (StreamWriter writer = new StreamWriter(request.EndGetRequestStream(ar))) { writer.Write("98052"); // Write input into body of request } request.BeginGetResponse(new AsyncCallback(OnGetResponseCompleted), request);} ...private void OnGetResponseCompleted(IAsyncResult ar) { ... }

demoHttpWebRequest

• Callable through WCF Web service proxies– Use Visual Studio's "Add Service Reference"

command to generate proxies• Selected WCF bindings supported

– BasicHttpBinding (WS-I Basic Profile 1.0)– Custom HTTP binding with binary message

encoding– No PollingDuplexHttpBinding or NetTcpBinding

• Asynchronous operation only

ASMX and WCF Services

• Use "Add New Item" -> "Silverlight-Enabled WCF Service" command in Visual Studio

Creating a WCF Service

Calling a WCF Service

ZipCodeServiceClient proxy = new ZipCodeServiceClient();proxy.GetCityAndStateFromZipCodeCompleted += new EventHandler<GetCityAndStateFromZipCodeCompletedEventArgs> (GetCityAndStateFromZipCodeCompleted);proxy.GetCityAndStateFromZipCodeAsync("98052"); ...private void GetCityAndStateFromZipCodeCompleted(object sender, GetCityAndStateFromZipCodeCompletedEventArgs e){ if (e.Error == null) { Place place = e.Result; // Method returns Place object string city = place.City; string state = place.State; ... }}

demoWCF Services

• Asynchronous notifications delivered to phones

• Utilize Microsoft Push Notification Service (PNS)– Hosted in Azure; massively scalable and reliable– Battery- and bandwidth-efficient alternative to

polling• Three types of notifications

– Raw notifications– Toast notifications– Tile notifications

• Toast and tile notifications work if app isn't running

Push Notifications

How Push Notifications Work

Microsoft PushNotification Service

YourWeb Service

Count

3

4

Phone transmits URI to Web service

Service calls PNSusing transmitted URI

5

12

PNS sends notification to phone

PNS returns URI

Phone app requests URI from PNS

• Delivered only when app is active– If app is inactive, call to PNS returns HTTP 200

OK with notification status == "Suppressed"• Payload can be text or binary data

– 1K maximum payload size

Raw Notifications

Raw Notification Wire Format

POST uri HTTP/1.1X-NotificationClass: intervalHost: uriContent-Type: application/*; charset=UTF-8Content-Length: length

payload

Sending a Raw Notification

HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;request.Method = WebRequestMethods.Http.Post;

request.Headers.Add("X-NotificationClass", "3"); // Send immediately

using (Stream stream = request.GetRequestStream()){ byte[] payload = Encoding.UTF8.GetBytes(message); stream.Write(payload, 0, payload.Length);}

HttpWebResponse response = request.GetResponse() as HttpWebResponse;

• Delivered even when application is inactive– Displayed in toast window at top of screen– Clicking toast window activates application

• Also delivered when application is active– Not automatically displayed– App decides what to do

Toast Notifications

Toast Notification Wire Format

POST uri HTTP/1.1X-NotificationClass: intervalX-WindowsPhone-Target: toastHost: uriContent-Type: application/*; charset=UTF-8Content-Length: length

<?xml version="1.0" encoding="utf-8"?><wp:Notification xmlns:wp="WPNotification"> <wp:Toast> <wp:Text1>MessageCaption</wp:Text1> <wp:Text2>MessageText</wp:Text2> </wp:Toast></wp:Notification>

Sending a Toast Notification

HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;request.Method = WebRequestMethods.Http.Post;

request.ContentType = "text/xml";request.Headers["X-WindowsPhone-Target"] = "toast";request.Headers.Add("X-NotificationClass", "2"); // Send immediately

using (Stream stream = request.GetRequestStream()){ byte[] payload = Encoding.UTF8.GetBytes (String.Format(_template, caption, message)); request.ContentLength = payload.Length; stream.Write(payload, 0, payload.Length);}

HttpWebResponse response = request.GetResponse() as HttpWebResponse;

• Update tiles pinned on start screen ("live tiles")– Change tile background image

• Local or remote images• Max size 80K; must load in under 15 seconds

– Change count displayed in tile's upper-right corner

– Change title displayed along tile's bottom border

Tile Notifications

Title

Count

Tile Notification Wire Format

POST uri HTTP/1.1X-NotificationClass: intervalX-WindowsPhone-Target: tileHost: uriContent-Type: application/*; charset=UTF-8Content-Length: length

<?xml version="1.0" encoding="utf-8"?><wp:Notification xmlns:wp="WPNotification"> <wp:Tile> <wp:BackgroundImage>BackgroundImageUri</wp:BackgroundImage> <wp:Count>Count</wp:Count> <wp:Title>Title</wp:Title> </wp:Tile> </wp:Notification>

Sending a Tile Notification

HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;request.Method = WebRequestMethods.Http.Post;

request.ContentType = "text/xml";request.Headers["X-WindowsPhone-Target"] = "token";request.Headers.Add("X-NotificationClass", "1"); // Send immediately

using (Stream stream = request.GetRequestStream()){ byte[] payload = Encoding.UTF8.GetBytes (String.Format(_template, imageuri, count, title)) request.ContentLength = payload.Length; stream.Write(payload, 0, payload.Length);}

HttpWebResponse response = request.GetResponse() as HttpWebResponse;

• Calls to PNS return important information in HTTP status codes and custom response headers– X-NotificationStatus– X-SubscriptionStatus– X-DeviceConnectionStatus

• Certain response codes must not be ignored– Subscription may be expired– Subscription may require server-side throttling

• Response codes documented at http://msdn.microsoft.com/en-us/library/ff941100(v=vs.92).aspx

PNS Response Codes

Sample Response Codes

HTTP Status

Notification Status

Subscription Status

Device StatusComments

200 Received Active Connected Notification was accepted

200 Received ActiveTempDisconnected

Notification was accepted, but device is disconnected

200 QueueFull Active Any valueNotification queue is full; try again later

200Suppressed

Active Any valueNotification could not be delivered because application is not active

404 Any value Any value ExpiredSubscription has expired and should be deleted; do not resend

406 Any value Any value Any valuePer-day quota has been reached; can try to resend once per hour

412 Any value Any value Any valueDevice is inactive; can try to resend once per hour, but with penalties

503 Any value Any value Any value PNS is unavailable

Handling Response Codes

HttpWebResponse response = request.GetResponse() as HttpWebResponse;

int status = (int)response.StatusCode;

string xsubscription = null;if (response.Headers["X-SubscriptionStatus"] != null) xsubscription = response.Headers["X-SubscriptionStatus"].ToString();

if ((xsubscription != null && xsubscription == "Expired") || status == 404 || status == 406 || status == 412){ // Delete the subscription RemoveSubscription(uri);}

• Class used to connect phone apps to PNS

HttpNotificationChannel

Method Description

BindToShellTile Binds channel to tile notifications

BindToShellToast Binds channel to toast notifications

Close Close an open channel

Find Returns an existing channel if that channel exists

Open Opens a channel

UnbindToShellTile Unbinds channel to tile notifications

UnbindToShellToast

Unbinds channel to toast notifications

Opening a Channel

channel = HttpNotificationChannel.Find("MyChannel");

if (channel == null) // Create a new channel{ channel = new HttpNotificationChannel("MyChannel"); RegisterChannelEventHandlers(channel); channel.Open(); // Generates ChannelUriUpdated event}else // Configure an existing channel{ RegisterChannelEventHandlers(channel); BindChannel(channel);

// TODO: Send the URI to the Web service}

Binding to Notifications

// Configure the channel to report toast notificationsif (!channel.IsShellToastBound) channel.BindToShellToast();

// Configure the channel to support tile notificationsif (!channel.IsShellTileBound) channel.BindToShellTile();

Handling Raw Notifications

channel.HttpNotificationReceived += OnRawNotificationReceived; . . .void OnHttpNotificationReceived(object sender, HttpNotificationEventArgs e){ // Transfer to UI thread if updating the UI Dispatcher.BeginInvoke(() => { // Payload in e.Notification.Body });}

Handling Toast Notifications

channel.ShellToastNotificationReceived += OnToastReceived; ...void OnToastReceived(object sender, NotificationEventArgs e){ // Transfer to UI thread if updating the UI Dispatcher.BeginInvoke(() => { string caption = String.Empty; if (e.Collection.ContainsKey("wp:Text1")) caption = e.Collection["wp:Text1"];

string message = String.Empty; if (e.Collection.ContainsKey("wp:Text2")) message = e.Collection["wp:Text2"];

... });}

Handling Tile Notifications

This space intentionally left blank

• Limit of one open channel per app• Limit of 15 open channels per device

– No way to know ahead of time how many are open

– HttpNotificationChannel.Open throws InvalidOperationException when limit is exceeded

• Limit of 500 notifications per day per channel– Limit waived for authenticated Web services

• Apps that use push notifications must comply with WP7 Application Certification Requirements

Constraints and Limitations

demoPush Notifications

• ShellTileSchedule class permits tiles to be updated periodically by downloading them from a server– Does not count against push notification limit– Frequency cannot exceed one update per hour

• Options are hourly, daily, weekly, monthly– Max image size 80K; must load in < 15 seconds

• Updates suspended when screen is locked or off– Updates fire immediately when screen unlocks

Tile Scheduling

Scheduling Hourly Updates

ShellTileSchedule sts = new ShellTileSchedule();

sts.Interval = UpdateInterval.EveryHour;sts.MaxUpdateCount = 0; // Run indefinitelysts.Recurrence = UpdateRecurrence.Interval;sts.RemoteImageUri = new Uri(@"http://www.wintellect.com/tiles/hourlyimage.png");

sts.Start();

Performing a One-Time UpdatexShellTileSchedule sts = new ShellTileSchedule();

sts.Recurrence = UpdateRecurrence.OneTime;sts.StartTime = DateTime.Now;sts.RemoteImageUri = new Uri(@"http://www.wintellect.com/tiles/tile.png");

sts.Start();

Detecting Connectivity Status

if (NetworkInterface.GetIsNetworkAvailable()){ // App is connected}else{ // App is not connected}

Detecting Status Changes

NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged; . . .void OnNetworkAddressChanged(object sender, EventArgs e){ if (NetworkInterface.GetIsNetworkAvailable()) { // App is connected } else { // App is not connected }}

Charles Petzoldwww.charlespetzold.com

Questions?

top related