Introduction to WebSockets with SignalR in .NET Part 3

Introduction

We’ll continue our discussion of SignalR where we left off in the previous post. So open the SignalRWeb demo project and let’s get to it!

Demo continued

We left off having the following “scripts” section in Index.cshtml:

@section scripts
{
    <script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
    <script src="~/Scripts/knockout-3.1.0.js"></script>
    <script src="~/Scripts/results.js"></script>
}

We’ll build on this example to see how a client can interact with the server through WebSockets. At present we have an empty Hello() method in our ResultsHub class. Insert another method which will allow the clients to send a message to the server:

public void SendMessage(String message)
{

}

There’s a property called Context that comes with SignalR. It is similar to HttpContext in ASP.NET: it contains a lot of information about the current connection: headers, query string, authentication etc. Just type “Context.” in the method body and inspect the available properties, they should be self-explanatory. We have no authentication so we’ll use the connection ID property of Context to give the sender some identifier:

public void SendMessage(String message)
{
	string completeMessage = string.Concat(Context.ConnectionId
		, " has registered the following message: ", message);

	Clients.All.registerMessage(completeMessage);
}

You’ll recall from the previous post that we need to deal with JavaScript in SignalR. The above piece of code will result in a JavaScript method called “registerMessage” to be invoked from the server. Where is that method? We need to write it of course. We inserted a JavaScript file called “results.js” previously. Open that file and enter the following stub:

(function () {
    var resultsHub = $.connection.resultsHub;
}());

resultsHub is a reference to the hub we’ve been working on. The “connection” property comes from the SignalR jQuery library and creates a SignalR connection. Through the connection property you’ll be able to reference your hubs. There’s no IntelliSense for the hub names, so be careful with the spelling.

We need to stop for a second and go back to Index.cshtml. There’s one more JavaScript source we need to reference, but it’s not available in the Scripts folder. Recall that we used the MapSignalR OWIN extension in Startup.cs. That extension will map the SignalR hubs to the /signalr endpoint. Add the following script declaration to the scripts section below the jquery.signalR-2.x.x.js script reference:

<script src="~/SignalR/hubs"></script>

Start the application and navigate to http://localhost:xxxxx/SignalR/hubs. You should see some JavaScript related to SignalR in the browser, so the script reference is valid. Basically this code generates proxies for the hubs. If you scroll down you’ll see that it has found the ResultsHub and its two dynamic methods:

proxies.resultsHub = this.createHubProxy('resultsHub'); 
proxies.resultsHub.client = { };
proxies.resultsHub.server = {
   hello: function () {
    return proxies.resultsHub.invoke.apply(proxies.resultsHub, $.merge(["Hello"], $.makeArray(arguments)));
   },

   sendMessage: function (message) {
      return proxies.resultsHub.invoke.apply(proxies.resultsHub, $.merge(["SendMessage"], $.makeArray(arguments)));
             }
};

As you add other hubs and other dynamic methods they will be registered here in this script generated by SignalR using Reflection. Note that the hello and sendMessage functions have been registered on the server – proxies.resultsHub.server – which is expected.

Add the following code to results.js just below the resultsHub reference:

$.connection.hub.logging = true;
$.connection.hub.start();

We turn on logging so that we can see what SignalR is doing behind the scenes. Then we tell SignalR to start the communication. This method will go through the 4 ways of establishing a connection with the client and determine which one works best.

Next we need the JavaScript methods that will be invoked: hello and registerMessage. Add the following stubs just below the call to hub.start():

resultsHub.client.hello = function(){

}

resultsHub.client.registerMessage = function (message) {

};

We’ll concentrate on the registerMessage function, hello can remain empty. Next we need to show the responses on the screen. As mentioned before we’ll use knockout.js as it provides for a very responsive GUI. I in fact only know the basics of knockout.js, as client side programming is not really my cup of tea. If you don’t know anything about knockout.js then you might want to go through the first couple of tutorials here. It is very much based on view-models which are bound to HTML elements. As the properties change so do the values of those elements in a seamless fashion. We won’t go into any detail about the knockout specific details here. Add the following code below the resultsHub.client.registerMessage stub:

var messageModel = function () {
      this.registeredMessage = ko.observable(""),
      this.registeredMessageList = ko.observableArray()
};

The registeredMessage property will show a message sent by the client. registeredMessageList will hold all messages that have been sent from the server. Next add a model prototype:

messageModel.prototype = {

        newMessage: function () {
            resultsHub.server.sendMessage(this.registeredMessage());
            this.registeredMessage("");
        },
        addMessageToList: function (message) {
            this.registeredMessageList.push(message);
        }

    };

newMessage is meant to be a function that can be invoked upon a button click. It sends the message to the server and then clears it. Recall that we called our function in ResultsHub “SendMessage”. You can call it using the “server” property followed by the name of the function you’d like to invoke. addMessageToList does exactly what the function name implies. Then we create an instance of the view-model and instruct knockout to start binding it to our GUI elements:

var viewModel = new messageModel();
$(function () {
        ko.applyBindings(viewModel);
    });

We can now fill in the resultsHub.client.registerMessage stub:

resultsHub.client.registerMessage = function (message) {
        viewModel.addMessageToList(message);
};

It simply calls upon the view-model instance to add the new message to the message list.

Now we can create the corresponding HTML code on index.cshtml. Add the following right above the scripts section.

<div>
    <input type="text" placeholder="Your message..." data-bind="value:registeredMessage" />
    <button data-bind="click:newMessage">Register message</button>
</div>

This bit of markup will serve as the “register” section where the user enters a message in the text box and sends it to the server using the button. Note the knockout-related data-bind attributes. The text box is bound to the registeredMessage property and the click event of the button is bound to the newMessage function of the model, both defined in our results.js file.

Add the following HTML below the “register” section:

<div>
    <div data-bind="foreach:registeredMessageList">
        <div data-bind="text: $data"></div>
    </div>
</div>

This is a knockout foreach expression: for each message in the registeredMessageList array we print the message in a separate div. We bind the text property of the div to the message. The current value in the foreach loop can be accessed using “$data” in knockout.

Let’s go through the expected process step by step:

  1. The user enters a message in the text box and presses the register message button
  2. The button invokes the newMessage function of the knockout view-model ‘messageModel’
  3. The newMessage function will invoke the SendMessage(string input) method of the ResultsHub.cs Hub
  4. The SendMessage function constructs some return message and calls the registerMessage JavaScript function on each client, in our case defined in results.js
  5. The regiesterMessage function receives the string returned by the SendMessage function of ResultsHub
  6. regiesterMessage adds the message to the messages array of the view-model
  7. The view-model is updated and knockout magically updates the screen with the new list of messages

Set a breakpoint within SendMessage of ResultsHub.cs. Start the application in Chrome and press F12 to open the developer tools. You should see some SignalR related messages in the log:

Connecting to WebSockets in Devtools

It has found the ResultsHub and managed to open the web socket endpoint. Note the protocol of ‘ws’. Now insert a message in the text box and press register. Code execution should stop at the breakpoint in Visual Studio meaning that we’ve managed to wire up the components correctly. I encourage you to inspect the Context property and see what’s available in it. You should then see your message under the textbox as returned by the SendMessage:

Message collection by SignalR

In the meantime the DevTools log should fill up with interesting messages such as:

  • SignalR: Invoking resultshub.SendMessage
  • SignalR: Triggering client hub event ‘registerMessage’ on hub ‘ResultsHub’.
  • SignalR: webSockets reconnecting.

You’ll see this third message if you stop at the breakpoint to check out the available properties. As this artificially slows down the response time the web sockets connection is lost and then re-opened.

If you want to talk to yourself on two different screens then open up another browser window, Firefox or IE, and navigate to the same localhost address as in Chrome. Then start sending messages. You should see them in both windows:

Messages in two browser windows

So a message sent from one browser is immediately propagated to all listeners by SignalR with minimal code from the developer.

Read the next post in this series here.

View the list of posts on Messaging here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: