Today’s users are interested in a rich experience where the terms client and server don’t mean a thing. They expect real-time action between both, no matter if the technology used is HTML5 websockets or something else. This session will cover SignalR and show you how it can be used to communicate in real time between the client and server, using HTML5 or not. Combine SignalR with ASP.NET MVC, JavaScript and perhaps a sprinkle of Azure and you’ll have an interesting, reliable and fast stack to build your real-time client-server and server-client communications. Join me on this journey between web, cloud and user. No toothpaste. Just code.
3. Who am I?
Maarten Balliauw
Antwerp, Belgium
Developer Advocate, JetBrains
Founder, MyGet
AZUG
Focus on web
ASP.NET MVC, Azure, SignalR, ...
Former MVP Azure & ASPInsider
Big passion: Azure
http://blog.maartenballiauw.be
@maartenballiauw
6. Users want the latest info, now!
Twitter – live searches/updates
Stock tickers
Auctions
Live scores
Real-time notifications
Collaborative apps
Live user analytics
Online gaming / browser games
… 6
7. HTTP is and old beast
Never designed for real-time communications
Web is request-response
Web is stateless
Websockets to the rescue!
8. Websockets
Extension to HTTP
Provide raw sockets over HTTP
Full-duplex, low latency
Traverses proxies
But…
Not every proxy server supports it
Not every webserver supports it
Not every browser supports it
Some antivirus blocks it
They are raw sockets! (protocol: up to you)
http://websocketstest.com/
9. Forever Frame
Server tells client that response is chuncked
Client keeps connection open untill server closes it
Server pushes data to the client followed by 0
Consumes server threads
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
<script>eval("... ")</script>0
<script>eval("... ")</script>0
10. Periodic polling
Poll from time to time using Ajax
Delay in communications due to polling interval
Wastes bandwidth & latency
Polling interval
11. Long polling
Poll but don’t respond until there’s data
Poll again after data received or after the connection times out
Consumes server threads & connection resources
14. SignalR
Three-in-one!
“Persistent” client/server connection over best transport
Connection negotiation
Abstracts away the transport
Provides just one programming model
http://github.com/signalr/signalr - open source
C#, JavaScript, UWP
https://github.com/aspnet/signalr - new .NET Core version
16. What just happened?
The server is broadcasting a message every few seconds
Clients are receiving messages
Code looks easy
No polling or whatsoever (at least not in my code)
SignalR decides on transport mechanism used based on client + server
18. Two programming models
PERSISTENT CONNECTION
Can communicate with 1..N clients
Requires a route to be defined
Limited to sending messages
You define the “protocol”
HUB
Abstraction over PersistentConnection
Can communicate with 1..N clients
Route automatically mapped (/signalr/hubs)
Can send messages and call methods
SignalR defines the protocol
20. Hubs
Hub methods can be called from client
Client methods can be called from hub
Target individual client
Target all clients
Target group of clients
http://shootr.signalr.net/
22. So far we have used...
On the server side
Host in any ASP.NET application (SignalR.Server)
On the client side
JavaScript (SignalR.JS)
But there’s more…
24. There is more!
On the server side
Host in any ASP.NET application (SignalR.Server) or using self-hosting (OWIN based)
Host on Azure
Scale out (Redis, Azure Service Bus)
(I did a NodeJS server as well, but don’t use it in production)
On the client side
JavaScript (SignalR.JS), Angular (AngularJs.SignalR.Hub)
Any .NET client (SignalR.Client)
UWP
iOS
Android
27. ASP.NET Core Sockets
SignalR in the ASP.NET Core world
Improvements based on SignalR learnings:
No more jQuery
No more iframe transport
No more message replay after reconnect (memory usage, performance)
No more Hub-state (I did not talk about this “ViewState” because it’s evil)
Endpoint per Hub (/signalr/hubs /signalr/...)
Scale-out now becomes scale-out (and not sending all messages everywhere)
Sticky sessions required (sad )
28. ASP.NET Core Sockets
New features:
Cross-platform (including things like RaspberryPi)
Binary data support (think sending images etc.)
Host-agnostic (HTTP works but also TCP/UDP/... sockets)
Protocol data format pluggable (JSON, protobuf, ...)
TypeScript client
...
29. Demo?
Will leave this one for you...
Under development! https://github.com/aspnet/signalr
Preview release mid-year
Release later this year
31. Conclusion
SignalR is three-in-one!
“Persistent” client/server connection over best transport
Abstracts away the transport
Provides just one programming model
Connections & Hubs
Connect various clients
Open VS2017
Create a new ASP.NET Empty Website
Install-Package Microsoft.AspNet.SignalR
Note server library, client script, jQuery dependency
Add startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(); }
}
Add a class:
public class TimeConnection
: PersistentConnection
{
}
Map the connection:
app.MapSignalR<TimeConnection>("/time");
Add an infinite loop:
ThreadPool.QueueUserWorkItem(_ =>
{
var connection = GlobalHost.ConnectionManager.GetConnectionContext<TimeConnection>();
while (true)
{
connection.Connection.Broadcast(DateTime.UtcNow.ToString());
Thread.Sleep(1000);
}
});
Add some 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-2.2.1.min.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>
Now let’s take things easy… How does all of this work?
Note that we are sending data TO THE CLIENT by just broadcasting on the connection
View Chrome developer tools, in this case web sockets
Add to web.config: (.vs\config\applicationhost.config)
<system.webServer>
<webSocket enabled="false"/>
</system.webServer>
View Chrome again, notice now event stream is used instead
Install-Package Microsoft.AspNet.SignalR.Sample and show stock ticker in two browsers
Add simple chat hub
[HubName("simplechat")]
public class SimpleChatHub : Hub
{
private static readonly Dictionary<string, string> _users = new Dictionary<string, string>();
public async Task Join(string username)
{
_users[Context.ConnectionId] = username;
await Clients.All.userJoined(username);
}
public async Task SendMessage(string message)
{
await Clients.All.messageReceived(
_users[Context.ConnectionId], message);
}
}
Explain: user joins, we store username passed linked to connection id
We then call back to all clients, calling a JS function on there
Client code:
<!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-2.2.1.min.js" type="text/javascript"></script>
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var username = prompt('What is your name?');
$.connection.hub.logging = true;
var hub = $.connection.simplechat;
// User joined
hub.client.userJoined = function (who) {
$("#messages").append('<li><em>' + who + ' joined the chat.</em></li>');
};
// Message received
hub.client.messageReceived = function (who, what) {
$("#messages").append('<li>' + who + ': ' + what + '</li>');
};
// Join the chat
$.connection.hub.start().done(function () {
hub.server.join(username);
});
// Bind client UI
$('#message').change(function () {
hub.server.sendMessage($('#message').val());
$('#message').val('');
$('#message').focus();
});
});
</script>
</head>
<body>
<h1>Chat</h1>
<ul id="messages"></ul>
<input type="text" id="message"/>
</body>
</html>
Explore hubs some more: different properties on the hub (clients all, per group, individual, ...)
Create a new Console application
Install-Package Microsoft.AspNet.SignalR.Client
Add code
var connection = new Connection("http://localhost:51736/time");
connection.Received += data =>
{
Console.Clear();
Console.WriteLine(data);
};
connection.Start();
Cool, so connection works! Now let’s try hubs and connect to our simple chat.
Add code
private static async Task RunAsync(string[] args)
{
_connection = new HubConnection("http://localhost:51736/signalr");
_hub = _connection.CreateHubProxy("simplechat");
_hub.On <string>("userJoined", userName =>
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"{userName} joined.");
Console.ResetColor();
});
_hub.On<string, string>("messageReceived", (userName, message) =>
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"{userName}: {message}");
Console.ResetColor();
});
await _connection.Start();
// Join the chat
Console.WriteLine("Your username:");
var myName = Console.ReadLine().Trim();
await _hub.Invoke("join", myName);
// Send messages
while (true)
{
var myMessage = Console.ReadLine().Trim();
if (!string.IsNullOrEmpty(myMessage))
{
await _hub.Invoke("sendMessage", myMessage);
}
}
}
Open DeckCast
Show people around and tell them about deck.js
Show the presenter mode and client mode on the web
Start a console based viewer
Start a console based presenter