Almost all HTML5 and web applications today make use of AJAX to communicate between client and server. AJAX is great with asynchronous operations, but what about real-time communication? How would you push a message from server to client? HTML5 WebSockets offer a solution to all of these questions but are not supported in every browser and every network environment. SignalR is the new cool kid on the block: it leverages WebSockets when supported and falls back to different mechanisms when they aren’t. In this session, I’ll show you where and how to use SignalR to create rich real-time applications in the browser. No toothpaste. Just code.
6. Twitter – live searches/updates
Stock streamers
Auctions
Live scores
Real-time notifications
Interactive games
Collaborative apps 6
Live user analytics
…
7. Never designed for real-time communications
HTML5 WebSockets to the rescue,
right?
8. Extension to HTTP
Provide raw sockets over HTTP
Full-duplex
Traverses proxies
It’s still a draft…
Not every proxy server supports it
Not every webserver supports it
Not every browser supports it
They are raw sockets!
21. PersistentConnection Hubs
Can communicate with 1..N clients Can communicate with 1..N clients
Is an IHttpHandler Abstraction over PersistentConnection
Requires a route to be defined Route automatically mapped (/signalr/hubs)
Limited to sending messages Can send messages and call methods
You define the “protocol” SignalR defines the protocol
That’s a lot of options, a lot of thingsto account forand a lot of different programmingmodels. Are yougoingto do this?Are youstillwriting separategetDocumentByIdandAttachEventmethodstoworkwith the DOM? WhataboutjQuery?
Open VS2010Create a new ASP.NET Empty WebsiteInstall-PackageSignalRNote server library, client script, jQuery dependencyInstall-Package SignalR.Sample and show stock ticker in two browsersNowlet’s take things easy… How does all of thiswork?----------------- without Internet connection ----------------------------Add a class:public class TimeConnection : PersistentConnection{}Add a route:RouteTable.Routes.MapConnection<TimeConnection>("time", "time/{*operation}"); Addaninfinite loop:ThreadPool.QueueUserWorkItem(_ => { var connection = Connection.GetConnection<TimeConnection>();while (true) {connection.Broadcast(DateTime.Now.ToString());Thread.Sleep(1000); } });Addsome HTML:<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { var connection = $.connection('time');connection.received(function(data) { $('h1').text('The time is ' + data); });connection.start(); }); </script></head><body> <h1>The time is now!</h1></body></html>----------------- without Internet connection --------------------------------------------- with Internet connection ----------------------------Install-Package LinqToTwitterAdd a class:public class TweetsConnection : PersistentConnection{}Add a route:RouteTable.Routes.MapConnection<TweetsConnection>("tweets", "tweets/{*operation}"); Addaninfinite loop:ThreadPool.QueueUserWorkItem(_ => { var connection = Connection.GetConnection<TweetsConnection>();while (true) {using (TwitterContext context = new TwitterContext()) { var tweets = context.Search.Where(t => t.Type == SearchType.Search && t.Query == "#test").SingleOrDefault().Entries;connection.Broadcast(tweets.ToList()); }Thread.Sleep(5000); } });Addsome HTML:<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { var connection = $.connection('tweets');connection.received(function (data) { $('#tweetsList').html('');for (var i = 0; i < data.length; i++) { $('#tweetsList').append($('<li>' + data[i].Content + '</li>')); } });connection.start(); }); </script></head><body> <h1>Tweets</h1> <ulid="tweetsList"></ul></body></html>----------------- with Internet connection ----------------------------Notice we are sending data TO the client?
TweetsusingKnockoutJS
Open the previouslycreated sampleInstall-Package jQuery.UI.CombinedAdd a class: [HubName("worker")] public class WorkerHub : Hub { public voidstartProcessing(Event e) {Caller.notify("We'vestarted processing " + e.EventName);Clients.setProgress(10);for (int i = 0; i <= 100; i++) {SignalR.Hubs.Hub.GetClients<WorkerHub>().setProgress(i);Thread.Sleep(100); } } }Addsome HTML:<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <title></title> <link rel="stylesheet" href="Content/themes/base/jquery.ui.all.css" /> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery-ui-1.8.16.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR.js" type="text/javascript"></script> <script src="signalr/hubs" type="text/javascript"></script> <script type="text/javascript"> $(function () { var workerHub = $.connection.worker; $('#progressbar').progressbar({ value: 0 }); $('#startWorkTrigger').bind('click', function () {workerHub.startProcessing({ eventName: 'UAN12' }) .fail(function (e) { alert("An error occured: " + e); }); });workerHub.notify = function (message) { $('#notifications').html(message); };workerHub.setProgress = function (progress) { $('#progressbar').progressbar({ value: progress }); }; $.connection.hub.start(); }); </script></head><body> <div id="progressbar" style="width: 200px;"></div> <div id="notifications" style="width: 200px;"></div> <button id="startWorkTrigger">Start work</button></body></html>
Create a new Windows Phone 7.1 project using the “WP DataBound Application”Install-Package SignalR.Client.WP7Uninstall-Package NewtonSoft.Json -ForceInstall-Package NewtonSoft.Json -Version 4.0.3In MainViewModel, remove the contents in LoadData(): public void LoadData() { if (!connection.IsActive) {connection.Start(); } }Add a field:private Connection connection; In the constructor, preparetoreceive data. Note the Dispatcherto make sure the adding of the data happens on the UI thread: public MainViewModel() {this.Items = new ObservableCollection<ItemViewModel>();connection = new Connection("http://localhost:6502/tweets");connection.Received += data => Deployment.Current.Dispatcher.BeginInvoke(() => UpdateItems(data));connection.Start(); }Add the UpdateItemsmethod:protectedvoidUpdateItems(string data) {this.Items.Clear();JArray entries = JArray.Parse(data);foreach (JObject entry in entries) { string content = Regex.Replace(entry["Content"].ToString(), "<.*?>", string.Empty);this.Items.Add(new ItemViewModel() { LineOne = entry["Title"].ToString().Substring(0, 12),LineTwo = ((JObject)entry["Author"])["Name"].ToString(), LineThree = content }); }this.IsDataLoaded = true; }
Open DeckCastShow peoplearoundandtellthemabout deck.jsShow the presenter mode andclient mode on the webStart a console based viewerStart the WP7 presenter app (focus/unfocus the “sample” box toconnect, click SLOWLY)