450 likes | 598 Views
Networking. Charles Petzold www.charlespetzold.com. Agenda. WebClient HttpWebRequest Consuming REST services Consuming ASMX and WCF services Push notification services Tile scheduling. Silverlight vs. SWP. WebClient. Event-based HTTP networking API Asynchronous operation only
E N D
Networking Charles Petzold www.charlespetzold.com
Agenda • WebClient • HttpWebRequest • Consuming REST services • Consuming ASMX and WCF services • Push notification services • Tile scheduling
WebClient • 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
Downloading an Image File WebClientwc = 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 File WebClientwc = 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 WebClientwc = new WebClient(); wc.DownloadProgressChanged+= OnDownloadProgressChanged; ... private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgse) { Progress.Value = e.ProgressPercentage; }
Downloading an RSS Feed WebClientwc = new WebClient(); wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OnOpenReadCompleted); wc.OpenReadAsync(new Uri("...")); ... private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgse) { if (e.Error == null) { using (StreamReader reader = new StreamReader(e.Result)) { string rss = reader.ReadToEnd(); // TODO: Parse the feed } } }
HttpWebRequest • 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
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(IAsyncResultar) { 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(IAsyncResultar) { 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(IAsyncResultar) { ... }
ASMX and WCF Services • 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
Creating a WCF Service • Use "Add New Item" -> "Silverlight-Enabled WCF Service" command in Visual Studio
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; ... } }
Push Notifications • 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
How Push Notifications Work Count Phone app requests URI from PNS Phone transmits URI to Web service 3 1 2 PNS returns URI 5 Service calls PNS using transmitted URI 4 PNS sends notification to phone Microsoft Push Notification Service Your Web Service
Raw Notifications • 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 Notification Wire Format POST uri HTTP/1.1 X-NotificationClass: interval Host: uri Content-Type: application/*; charset=UTF-8 Content-Length: length payload
Sending a Raw Notification HttpWebRequestrequest = 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;
Toast Notifications • 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 Notification Wire Format POST uri HTTP/1.1 X-NotificationClass: interval X-WindowsPhone-Target: toast Host: uri Content-Type: application/*; charset=UTF-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <wp:Notificationxmlns:wp="WPNotification"> <wp:Toast> <wp:Text1>MessageCaption</wp:Text1> <wp:Text2>MessageText</wp:Text2> </wp:Toast> </wp:Notification>
Sending a Toast Notification HttpWebRequestrequest = 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;
Tile Notifications • 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 Count Title
Tile Notification Wire Format POST uri HTTP/1.1 X-NotificationClass: interval X-WindowsPhone-Target: tile Host: uri Content-Type: application/*; charset=UTF-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <wp:Notificationxmlns: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 HttpWebRequestrequest = 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;
PNS Response Codes • 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
Handling Response Codes HttpWebResponse response = request.GetResponse() as HttpWebResponse; intstatus = (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); }
HttpNotificationChannel • Class used to connect phone apps to PNS
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 notifications if (!channel.IsShellToastBound) channel.BindToShellToast(); // Configure the channel to support tile notifications if (!channel.IsShellTileBound) channel.BindToShellTile();
Handling Raw Notifications channel.HttpNotificationReceived+= OnRawNotificationReceived; . . . void OnHttpNotificationReceived(object sender, HttpNotificationEventArgse) { // 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
Constraints and Limitations • 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
Tile Scheduling • 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
Scheduling Hourly Updates ShellTileSchedulests = new ShellTileSchedule(); sts.Interval = UpdateInterval.EveryHour; sts.MaxUpdateCount = 0; // Run indefinitely sts.Recurrence = UpdateRecurrence.Interval; sts.RemoteImageUri= new Uri(@"http://www.wintellect.com/tiles/hourlyimage.png"); sts.Start();
Performing a One-Time Update x ShellTileSchedulests = 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 } }
Questions? Charles Petzold www.charlespetzold.com