Real-time Web & Push Services with Hubs
by Christian Weyer (christian.weyer@thinktecture.com)
Developers of SignalR on https://jabbr.net
Push Services pattern (models a service that)
- accepts incoming connections from callers
- is able to push data down to callers
- abstracts from communication nitty-gritty details
- consumers send to push service
- push service notifies consumers
- external events (Db, Message Queue, ...) trigger push service
HTTP is the protocol
- Periodic Polling
- Long Polling (http streaming/comet) -> server only responds when there is data
- Forever Frame (connection within an iframe)
- Server-Sent Events (SSE)
- Web Sockets (only Windows 8/Server 2012, network considerations/blockings)
Hubs
- public methods are callable from the outside
- send messages to clients by invoking client-side methods
- [HubName("chat")]
- Context holds connection- and request-specific information
- ConnectionId, Request, Headers, RequestCookies, QueryString, User
- First register the hubs and then add your other routes.
- Target method with parameters is dynamically 'injected' into code path, serialized, and embedded into protocol response
- Groups are not persisted on the server
- We need to keep track of what connections are in chat groups
- No automatic group count
Clients.Caller.newMessage(message);
Clients.Client(Context.ConnectionId).newMessage(message);
Clients.All.newMessage(message);
Clients.Others.newMessage(message);
Clients.AllExcept(Context.ConnectionId).newMessage(message);
Groups.Add(Context.ConnectionId, room);
Clients.Group(room).newMessage(msg);
public override Task OnConnected() {}
public override Task OnDisconnected() {}
public override Task OnReconnected() {}
// Sending data from outside a hub, retrieve hub context via dependency resolver
private void SendMonitorData(string eventType, string connection)
{
var context = GlobalHost.ConnectionManager.GetHubContext<MonitorHub>();
context.Clients.All.newEvent(eventType, connection);
}
Clients
- .NET4.0+, WinRT, Windows Phone 8, Silverlight 5, jQuery, C++, iOS native, iOS via Mono, Android via Mono
- /signalr/hubs is the dynamic proxy contract
- use signalr.exe ghp /url:http://localhost:31373 tool to create a static proxy
Javascript with proxy
$.connection.hub.logging = true;
chat = $.connection.chat
$.connection.hub.start({ transport:'longPolling' });
$.connection.hub.connectionSlow = onConnectionSlow;
Javascript without proxy
var connection = $.hubConnection();
var proxy = connection.createHubProxy('chat');
proxy.on('newMessage', onNewMessage);
connection.start();
proxy.invoke('sendMessage', $('#message').val());
.Net
var hubConnection = new HubConnection("http://localhost/ps");
var chat = hubConnection.CreateHubProxy("chat");
chat.On<string>("newMessage", msg => messages.Invoke(new Action(() => messages.Items.Add(msg));
hubConnection.Start().Wait();
// Opened, Closed, Error, Received, Reconnected, Reconnecting, StateChanged
Hosting
OWIN (Open Web Server Interface for .Net) - http://owin.org
Katana project - http://katanaproject.codeplex.com
- Application delegate (AppFunc) var AppFunc = Func<IDictionary<string, object>, Task>;
- Environment dictionary
- ASP.Net hosting sits on top of OWIN
- Self-hosting
- Microsoft.Owin.Hosting aka Katana
- Microsoft.Owin.Host.HttpListener
// process user needs http.sys permissions on the url namespace
public void Configuration(IAppBuilder app)
{
var a = Assembly.LoadFrom("Services.dll");
app.MapHubs(new HubConfiguration { EnableCrossDomain = true });
}
using(WebApplication.Start<Startup>("http://localhost:6789")
{
Console.WriteLine("Hubs running...");
Console.ReadLine();
}
// jQuery client needs to explicitly set connection url
$.connection.hub.url = "http://localhost:6789/signalr";
Azure
- Which server instance handles the client request?
- Which instance pushes?
Other aspects
- Use SignalR message bus
- SignalR extensible architecture
- Persistent connections
- Scale-Out
- WebApi Integration
- Security