Introduction into SignalR 2.0 & Azure Website Websockets

 

image.png

SignalR is an Open Source Framework for Real Time WebApps. The main problem with Real-Time in the web is the canal between Browser and Server. If you never had to deal with SignalR and this problem before here is a brief introduction:

 

The problem

Traditionally the browser initiates the request to the server and the server reacts with an answer. If the server tries to send some information in return the options are limited.

 

The “solution”

The oldest way is via “Comet” which is a collection of tricks. That means the client initiates a request and it won’t close until informations from the server are transmitted. Principally the connection is open all the time. Well this isn’t the classiest option.

 

Another way is to use the so called “Server Sent Events” which are supported by almost every browser – besides IE. Older IEs have to use the “Comet” way.

 

The latest alternatives are so called WebSockets. A TCP connection between server and client is build and at the same time a bidirectional in the opposite direction.

 

The problem with WebSockets

WebSockets have to be supported by not only the browser but also by the server. In IIS they are official supported since IIS 8.0. That means only with Windows Server 2012 and above. Before that there is no way to activate them in the IIS pipeline.

 

The browser supports WebSockets since IE 10. Chrome and Firefox offer the support also in earlier versions and the feature is implemented in all modern browsers.

 

SignalR – real time for everyone

Since there are several ways of “protocol-types” that depend on support from both the client and the server it is not inappropriate to build and App. That’s where SignalR gets into the game. SignalR builds the best possible connection automatically and also it has a very impressive program model. SignalR is open source and the code is available on GitHub. Still you will get support from Microsoft (if needed).

 

SignalR DemoHub

That’s an example from the GitHub account:

 

public class DemoHub : Hub 2: { 3: public override Task OnConnected() 4: { 5: return Clients.All.hubMessage("OnConnected " + Context.ConnectionId); 6: } 7: 8: public override Task OnDisconnected() 9: { 10: return Clients.All.hubMessage("OnDisconnected " + Context.ConnectionId); 11: } 12: 13: public override Task OnReconnected() 14: { 15: return Clients.Caller.hubMessage("OnReconnected"); 16: } 17: 18: public void SendToMe(string value) 19: { 20: Clients.Caller.hubMessage(value); 21: } 22: 23: public void SendToConnectionId(string connectionId, string value) 24: { 25: Clients.Client(connectionId).hubMessage(value); 26: } 27: 28: public void SendToAll(string value) 29: { 30: Clients.All.hubMessage(value); 31: } 32: 33: public void SendToGroup(string groupName, string value) 34: { 35: Clients.Group(groupName).hubMessage(value); 36: } 37: 38: public void JoinGroup(string groupName, string connectionId) 39: { 40: if (string.IsNullOrEmpty(connectionId)) 41: { 42: connectionId = Context.ConnectionId; 43: } 44: 45: Groups.Add(connectionId, groupName); 46: Clients.All.hubMessage(connectionId + " joined group " + groupName); 47: } 48: 49: public void LeaveGroup(string groupName, string connectionId) 50: { 51: if (string.IsNullOrEmpty(connectionId)) 52: { 53: connectionId = Context.ConnectionId; 54: } 55: 56: Groups.Remove(connectionId, groupName); 57: Clients.All.hubMessage(connectionId + " left group " + groupName); 58: } 59: 60: public void IncrementClientVariable() 61: { 62: Clients.Caller.counter = Clients.Caller.counter + 1; 63: Clients.Caller.hubMessage("Incremented counter to " + Clients.Caller.counter); 64: } 65: 66: public void ThrowOnVoidMethod() 67: { 68: throw new InvalidOperationException("ThrowOnVoidMethod"); 69: } 70: 71: public async Task ThrowOnTaskMethod() 72: { 73: await Task.Delay(TimeSpan.FromSeconds(1)); 74: throw new InvalidOperationException("ThrowOnTaskMethod"); 75: } 76: 77: public void ThrowHubException() 78: { 79: throw new HubException("ThrowHubException", new { Detail = "I can provide additional error information here!" }); 80: } 81: 82: public void StartBackgroundThread() 83: { 84: BackgroundThread.Enabled = true; 85: BackgroundThread.SendOnPersistentConnection(); 86: BackgroundThread.SendOnHub(); 87: } 88: 89: public void StopBackgroundThread() 90: { 91: BackgroundThread.Enabled = false; 92: } 93: }

The server defines a hub and it calls “client-functions” with the API like for example “hubMessage”.

The method is defined in Javascript and SignalR caters for the call:

 

1: function writeError(line) { 2: var messages = $("#messages"); 3: messages.append("<li style='color:red;'>" + getTimeString() + ' ' + line + "</li>"); 4: } 5: 6: function writeEvent(line) { 7: var messages = $("#messages"); 8: messages.append("<li style='color:blue;'>" + getTimeString() + ' ' + line + "</li>"); 9: } 10: 11: function writeLine(line) { 12: var messages = $("#messages"); 13: messages.append("<li style='color:black;'>" + getTimeString() + ' ' + line + "</li>"); 14: } 15: 16: function getTimeString() { 17: var currentTime = new Date(); 18: return currentTime.toTimeString(); 19: } 20: 21: function printState(state) { 22: var messages = $("#Messages"); 23: return ["connecting", "connected", "reconnecting", state, "disconnected"][state]; 24: } 25: 26: function getQueryVariable(variable) { 27: var query = window.location.search.substring(1), 28: vars = query.split("&"), 29: pair; 30: for (var i = 0; i < vars.length; i++) { 31: pair = vars[i].split("="); 32: if (pair[0] == variable) { 33: return unescape(pair[1]); 34: } 35: } 36: } 37: 38: $(function () { 39: var connection = $.connection.hub, 40: hub = $.connection.demoHub; 41: 42: connection.logging = true; 43: 44: connection.connectionSlow(function () { 45: writeEvent("connectionSlow"); 46: }); 47: 48: connection.disconnected(function () { 49: writeEvent("disconnected"); 50: }); 51: 52: connection.error(function (error) { 53: writeError(error); 54: }); 55: 56: connection.reconnected(function () { 57: writeEvent("reconnected"); 58: }); 59: 60: connection.reconnecting(function () { 61: writeEvent("reconnecting"); 62: }); 63: 64: connection.starting(function () { 65: writeEvent("starting"); 66: }); 67: 68: connection.stateChanged(function (state) { 69: writeEvent("stateChanged " + printState(state.oldState) + " => " + printState(state.newState)); 70: var buttonIcon = $("#startStopIcon"); 71: var buttonText = $("#startStopText"); 72: if (printState(state.newState) == "connected") { 73: buttonIcon.removeClass("glyphicon glyphicon-play"); 74: buttonIcon.addClass("glyphicon glyphicon-stop"); 75: buttonText.text("Stop Connection"); 76: } else if (printState(state.newState) == "disconnected") { 77: buttonIcon.removeClass("glyphicon glyphicon-stop"); 78: buttonIcon.addClass("glyphicon glyphicon-play"); 79: buttonText.text("Start Connection"); 80: } 81: }); 82: 83: hub.client.hubMessage = function (data) { 84: writeLine("hubMessage: " + data); 85: } 86: 87: $("#startStop").click(function () { 88: if (printState(connection.state) == "connected") { 89: connection.stop(); 90: } else if (printState(connection.state) == "disconnected") { 91: var activeTransport = getQueryVariable("transport") || "auto"; 92: connection.start({ transport: activeTransport }) 93: .done(function () { 94: writeLine("connection started. Id=" + connection.id + ". Transport=" + connection.transport.name); 95: }) 96: .fail(function (error) { 97: writeError(error); 98: }); 99: } 100: }); 101: 102: $("#sendToMe").click(function () { 103: hub.server.sendToMe($("#message").val()); 104: }); 105: 106: $("#sendToConnectionId").click(function () { 107: hub.server.sendToConnectionId($("#connectionId").val(), $("#message").val()); 108: }); 109: 110: $("#sendBroadcast").click(function () { 111: hub.server.sendToAll($("#message").val()); 112: }); 113: 114: $("#sendToGroup").click(function () { 115: hub.server.sendToGroup($("#groupName").val(), $("#message").val()); 116: }); 117: 118: $("#joinGroup").click(function () { 119: hub.server.joinGroup($("#groupName").val(), $("#connectionId").val()); 120: }); 121: 122: $("#leaveGroup").click(function () { 123: hub.server.leaveGroup($("#groupName").val(), $("#connectionId").val()); 124: }); 125: 126: $("#clientVariable").click(function () { 127: if (!hub.state.counter) { 128: hub.state.counter = 0; 129: } 130: hub.server.incrementClientVariable(); 131: }); 132: 133: $("#throwOnVoidMethod").click(function () { 134: hub.server.throwOnVoidMethod() 135: .done(function (value) { 136: writeLine(result); 137: }) 138: .fail(function (error) { 139: writeError(error); 140: }); 141: }); 142: 143: $("#throwOnTaskMethod").click(function () { 144: hub.server.throwOnTaskMethod() 145: .done(function (value) { 146: writeLine(result); 147: }) 148: .fail(function (error) { 149: writeError(error); 150: }); 151: }); 152: 153: $("#throwHubException").click(function () { 154: hub.server.throwHubException() 155: .done(function (value) { 156: writeLine(result); 157: }) 158: .fail(function (error) { 159: writeError(error.message + "<pre>" + connection.json.stringify(error.data) + "</pre>"); 160: }); 161: }); 162: });

 

What’s new in SignalR 2.0?

Damian Edwards created a nice demo project on his GitHub account:

image

 

Azure Websites & Websockets

Since about a month Azue Websites support Websockets as well. In default mode the websocket support is deactivated. You can change the settings in the Azure management portal:

 

image

If you run the SignalR demo application without the websocket support that’s what the traffic looks like:

 

image

 

And with the support:

image

What’s great on SignalR: the “transportation way” is unappealing because SignalR takes care of this for you so you can concentrate on the main functionalities.

 

SignalR Resources

For more informations follow these links:

- ASP.NET SignalR Tutorial

- SignalR “JabbR” room . Chat (build with SignalR) where the developers often hang out

- SignalR Account on GitHub

This video by two of the main developers of SignalR is very impressive:

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

About the author

Written by

Learn more about our team.

Comment on this post

Recent Posts

  • How to access an Azure Website with the local IIS Manager

      Since the end of February it is possible to access an Azure Website with the IIS Manager. Although the Azure Management site offers some information’s there are more details visible at the IIS Manager.   For the connection you will need an IIS Manager and the IIS Manager for Remote Administration Extension. It’s also […]

  • image1929-570x143_thumb.png
    Create and validate own Json-Web-Tokens (JWTs)

    If you are interested in web authentication you probably have heard about JSON Web tokens (JWT). What is a JWT? Maybe I’m not using the correct security termination but however: JWTs are used to exchange claims between two systems. For example: You want to log on to a service (like Facebook, Twitter, etc.) and want […]

  • Micro-Optimization: how to shrink or „embed“ pictures

      I’m currently working on the “CodeInside Dashboard” and since the page structure isn’t that difficult it should be possible to fulfill all of Google Pagespeed or Yahoos YSlow recommendations. One of the rules was to optimize the 4 PNGs that are embedded on the page.   Before – without optimization: Below you can see […]

  • image1979-570x194.png
    Move to Windows Azure – VMs, Word Press Migration, DNS changes

    Since mid January this blogs runs on a WordPress installation in an Azure VM. Because I always thought that the subject is quite complicated this blogpost offers a view behind the scenes. Why this move? So far this blog (both German and English Version) runs on a hoster somewhere in Germany. The main problem with this […]

  • Windows Azure Active Directory – CRUD for users and groups

      Windows Azure Active Directory? If you are not informed about the subject I recommend you to have a look on this Azure Info site. Which resources are there? The Azure AD contains the following entities: - Users - Groups - Contacts - Roles Access to the directory or on the “directory graph” Although the […]

Support us