Introduction
In this article, I will introduce the very basic knowledge of ASP.NET SignalR I have learnt recently and demonstrate the simplest demo (SignalREcho_no_packages.zip) using PersistentConnection
step by step. Note that I have deleted the packages folder from the solution because the size is too big for uploading. You could fetch Microsoft.AspNet.SignalR locally via NuGet for build. OK, let’s start from some basic questions.
What can ASP.NET SignalR do?
- Server pushes data to client in HTTP connection
- Server to client RPC in HTTP connection
- Easily scale out via SQL Server, Redis or Service Bus
What technologies does SiganlR use to transfer data?
- Using WebSockets if it is available
- Otherwise, using other technologies, e.g. long polling
What kind of data does SignalR support to transfer?
- Non-compressed JSON text or plain text
- If you want to transfer compressed JSON or BSON or your binary data, you have to encode and decode the data (e.g. using Base64) or to implement
IJsonSerializer
by your own logic on the server side and implement the same logic on the client side. Note that TextReader
and TextWriter
are used inside SignalR. That means you have to convert your data to text before transferring anyway.
The current stable version is 1.1.3. Version 2.0 is in Beta. The license is Apache 2.0. The official web site is http://signalr.net. The documentation can be found here.
Packages
All SignalR development packages can be fetched through NuGet. Normally, you just need to fetch Microsoft.AspNet.SignalR
(Package ID) which contains most of the components you need, including:
Microsoft.AspNet.SignalR.Core
: server side components used to build SignalR endpoints. Microsoft.AspNet.SignalR.Owin
: OWIN host for SignalR. Microsoft.AspNet.SignalR.SystemWeb
: used to host SignalR in ASP.NET (via OWIN ASP.NET host). Microsoft.AspNet.SignalR.Js
: JavaScript client for SignalR. - Dependencies are also included: jQuery,
Newtonsoft.Json
, Microsoft.Owin.Host.SystemWeb
, Microsoft.Web.Infrastructure
and OWIN.
You could fetch other SignalR packages when you need:
Microsoft.AspNet.SignalR.Client
: .NET client for SignalR (includes WinRT, WP8 and Silverlight5). Microsoft.AspNet.SignalR.Redis
: Redis messaging backplane for scaling out SignalR in web farm. Microsoft.AspNet.SignalR.ServiceBus
: Windows Azure Service Bus messaging backplane for scaling out SignalR in web farm. Microsoft.AspNet.SignalR.SqlServer
: SQL Server messaging backplane for scaling out SignalR in web farm. Microsoft.AspNet.SignalR.Sample
: used to quickly setup a working sample in your application.
PersistentConnection
PersistentConnection
is the core class used to host a SignalR service on the server side. Let me demonstrate how to use it.
First, I create a new ASP.NET web application and use NuGet to fetch the Microsoft.AspNet.SignalR
package. Then, I define a new class inherited from PersistentConnection
:
public class MyConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
string msg = string.Format(
"A new user {0} has just joined. (ID: {1})",
request.QueryString["name"], connectionId);
return Connection.Broadcast(msg);
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
string msg = string.Format(
"{0}: {1}", request.QueryString["name"], data);
return Connection.Broadcast(msg);
}
}
OnConnected
is called when a client connects to my service. Connections are differentiated by connection IDs (GUID). The client can pass some information through IRequest.QueryString
when the connection is established. We can also get other HTTP items like cookies, posted form, headers and user principal from IRequest
. Note all these items belong to the first request which requests to establish the connection.
OnReceived
is called when client data is arrived. connectionId
tells which connection the data comes from. The data is in text format. You have to parse it by yourself if it does not represent plain text.
PersistentConnection.Connection
provides the Broadcast
method to send a message to all connected clients; it also provides the Send
method to send a message to a specific connection or a group (using the group name as connectionId
). Both methods support an excluded list for connections that we don’t want to send messages to.
We can register our service to the route table in Global.asax using MyConnection
:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapConnection<MyConnection>("echo", "/echo");
}
}
Now we can write JavaScript in a web page to connect to our service:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SignalR Echo</title>
<script type="text/javascript" src="Scripts/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="Scripts/jquery.signalR-1.1.3.min.js"></script>
</head>
<body>
<script type="text/javascript">
function htmlEncode(value) {
return $("<div/>").text(value).html();
}
function addMsg(msg) {
$("#messages").append("<li>" + htmlEncode(msg) + "</li>");
}
$(function () {
$("#join").click(function () {
var connection = $.connection("/echo", "name=" + $("#name").val(), true);;
connection.received(function (data) {
addMsg(data);
});
connection.error(function (err) {
addMsg("Error: " + err);
});
addMsg("Connecting...");
connection.start(function () {
addMsg("Connected.");
$("#send").click(function () {
connection.send($("#msg").val());
});
});
});
});
</script>
<table>
<tr>
<td><span>Name:</span></td>
<td><input type="text" id="name" /></td>
<td><input type="button" value="Join" id="join" /></td>
</tr>
<tr>
<td><span>Message:</span></td>
<td><input type="text" id="msg" /></td>
<td><input type="button" value="Send" id="send" /></td>
</tr>
</table>
<ul id="messages"></ul>
</body>
</html>
Next let’s create a .NET client. Just create a new console application and use NuGet to fetch the Microsoft.AspNet.SignalR.Client
package. In Main
, I use the Connection class to communicate with our service:
class Program
{
private const string ServiceUri = "http://localhost:12722/echo";
static void Main(string[] args)
{
var connection = new Connection(ServiceUri, "name=dzy");
connection.Received += connection_Received;
connection.StateChanged += connection_StateChanged;
Console.WriteLine("Connecting...");
connection.Start().Wait();
string inputMsg;
while (!string.IsNullOrEmpty(inputMsg = Console.ReadLine()))
{
connection.Send(inputMsg).Wait();
}
connection.Stop();
}
static void connection_StateChanged(StateChange state)
{
if (state.NewState == ConnectionState.Connected)
{
Console.WriteLine("Connected.");
}
}
static void connection_Received(string data)
{
Console.WriteLine(data);
}
}
To see whether the service is running on WebSockets or long polling, we can host the service on both IIS 7.5 and IIS 8.0 on Windows 8 or Windows Server 2012. Note that .NET WebSockets only works on Windows 8, Windows Server 2012 and above. Then, we can use Fiddler to check the HTTP requests on the client machine.
If the service is hosted on IIS 7.5, the connecting request keeps alive:
If the service is hosted on IIS 8 on Windows 8 or Windows Server 2012, the connecting request asks for upgrading to WebSocket connection:
Summary
In the next part, I will introduce SignalR Hubs, Scaling Out and Extensibility.