Let's Chat Application Using SignalR in MVC
SignalR is an open and free library which can be used to integrate real-time functionality in your web applications.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
Here, I will be demonstrating an application to chat, including private chat, using SignalR.
First, we need to know what SignalR is! SignalR is an open and free library which can be used to integrate real-time functionality in your web applications. There are a lot of areas where SignalR can come in handy to make your application better, more integrated, and more responsive to the end user. Real-time means having your server respond as quickly as possible to the client when a request is made.
For example, we may have a requirement to show the user uploading a file and the percentage of that file that has been uploaded to the server. Or, perhaps we have a scenario where we have to show progress to the end user for uploading and processing a CSV file with 'n' number of rows, where each row has some validation. The end user may be wondering what is going on in the back-end, so it'd be great if we could show him, similar to a progress window, how many rows have been processed and how many are left! Here comes the SignalR magic!
Most of us think SignalR would be useful in making chat applications, but it has much more than just chat! I don’t think the makers of SignalR would have a thought in mind to make a chat application out of it!
Enough of the story! Let's get into a bit of theory!
Theory
We will look at a simple image below and from it, try and gain some knowledge about the flow:
Nowadays, every application needs to load a server response in real time to gain traction in the market, as user expectations are higher.
Remote Procedure Calls (RPC) is the concept that takes place in SignalR internally. SignalR provides an API which helps in making the RPC between the server and the client. Actually, from the client side, server side functions are called using JavaScript, once the connection to the server is set. The SignalR API also helps create connections and manage them when required. In simple terms, SignalR provides the connection between server and client, letting the server call the functions on the client side and the client side to call the server side. That somehow is called "Server-Push".
SignalR starts with HTTP and then changes to a WebSocket if the connection is available. From the wiki: "WebSocket is a protocol providing full-duplex communication channels over a single TCP connection."
An advantage of using WebSocket is it can be used by both client and server applications. WebSocket is considered to be the most efficient and consistent medium of communication as it has the ability to manage server memory in a proper manner. Being a full-duplex communication, it has low latency. These are the considerations made with SignalR which make it more efficient.
SignalR decides the transport based on the browser; i.e. which required transport the browser supports. We will discuss the kinds of transports next:
HTML 5 Transports
- WebSockets we have already discussed. This transport is considered to be true-persistent, creating a two-way connection between client and server if the browsers support it.
- Server Sent events are also called Event Source, which is supported by all browsers except IE.
Comet Transports
Comet is a web application model in which a long-held HTTP request allows the server to post data to a client (browser).
- Forever frame This is supported by Internet Explorer only. Creates a hidden frame making a request to an endpoint on the server. The server keeps pinging the client or sends a script to the client, thus providing a one-way connection, creating a new connection on every request.
- AJAX Polling This is actually long polling, which is never persistent. This polls the request and waits until and unless the response is received from the server. This introduces latency and connection resets.
Practical
We will be creating a chat application in order to explain the flow of SignalR. We install SignalR, create a hub with which the client will interact. The client calls the server methods and in return the server responds and interacts with the client.
You can directly add a new project in VS for the SignalR or create an MVC project and install the SignalR package/libraries from NuGet.
PM > Install-Package Microsoft.AspNet.SignalR
This downloads all the dependencies required for SignalR.
After a successful installation, the above DLLs or packages are installed into your project.
There will be a class file which needs to be added to the root of your project, which would look like:
using Owin;
using Microsoft.Owin;
[assembly: OwinStartup(typeof(SignalRChat.Startup))]
namespace SignalRChat
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
}
This is an OWIN based application. Every OWIN application will have a startup.cs class, where the component for the application pipeline is added. The OWIN attribute specifies the type of property, specifying the project’s start up and the configuration method, and also sets up the SignalR mapping for the App.
There will be another two script files that will be added as we install the packages for SignalR.
These script files are mandatory to be loaded onto the .cshtml page in order to activate SignalR.
Let's look into the code straight away:
We need to add a new hub class inside a Hub folder. Let's name that LetsChatHub.cs, which would look like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace LetsChatApplication.Hubs
{
public class LetsChatHub : Hub
{
public void Send(string name, string message,string connId)
{
Clients.Client(connId).appendNewMessage(name, message);
}
}
}
The above send method accepts the parameters, name (which you will give once you navigate to the URL), and the message (which the user will send from the UI). The other parameter is connId, which would help us have a private chat and not send the message to every user who navigates to the site. To allow every user access to every message, the code you change is below:
namespace LetsChatApplication.Hubs
{
public class LetsChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.appendNewMessage(name, message);
}
}
}
The Send method is requested from the client with the parameters after the connection is set on the client side. Once the server receives the request, it processes and sends back the response to the client, using appendNewMessage. This appendNewMessage method is added on the client side to receive the response and display it in the UI to the client.
You need to add a controller. Let's call it "LetsChat" with an action "LetsChat", and add a view to that.
The client side code would look like below:
@{
ViewBag.Title = "LetsChat";
}
<h2>Lets Chat</h2>
<link href="~/Content/sweetalert.css" rel="stylesheet" />
<div class="form-group col-xl-12">
<label class="control-label">Your connection Id</label><br />
<input type="text" class="col-lg-12 text-primary" id="frndConnId" placeholder="Paste your friend's connection Id" /><br /><br />
<label class="control-label">Your Message</label><br />
<textarea type="text" class="col-lg-10 text-primary" id="message"></textarea>
<input type="button" class="btn btn-primary" id="sendmessage" value="Send" /><br /><br />
<img src="~/Content/smile.jpg" width="20" height="20" id="smile" style="cursor:pointer"/>
<img src="~/Content/uff.jpg" width="20" height="20" id="ufff" style="cursor:pointer" />
<div class="container chatArea">
<input type="hidden" id="displayname" />
<ul id="discussion"></ul>
</div>
</div>
<br />
<input type="hidden" id="connId" />
<!--Reference the autogenerated SignalR hub script. -->
@section scripts {
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Content/sweetalert.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
//var userName = "";
//var sessionVal = '';
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.letsChatHub;
debugger;
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
// Get the user name and store it to prepend to messages.
swal({
title: "Lets Chat!",
text: "<span style='color:#f8bb86;font-weight:700;'>Enter your name:</span>",
type: "input",
html: true,
showCancelButton: true,
closeOnConfirm: true,
animation: "slide-from-top",
inputPlaceholder: "Your Name"
},
function (inputValue) {
userName = inputValue;
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to type your name!");
return false;
}
$('#displayname').val(inputValue);
});
// Set initial focus to message input box.
$('#message').focus();
$('#message').keypress(function (e) {
if (e.which == 13) {//Enter key pressed
$('#sendmessage').trigger('click');//Trigger search button click event
}
});
$("#smile").click(function () {
});
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
var connId = $("#connId").val();
var frndConnId = $("#frndConnId").val();
var finalConnId = frndConnId == "" ? $.connection.hub.id : frndConnId;
chat.server.send($('#displayname').val(), $('#message').val(), finalConnId);
$("#connId").val($.connection.hub.id);
if (frndConnId == "") {
swal("You connection Id", $.connection.hub.id, "success");
}
// Clear text box and reset focus for next comment.
$('#discussion').append('<li><strong>' + htmlEncode($('#displayname').val())
+ '</strong>: ' + htmlEncode($('#message').val()) + '</li>');
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
Let's Chat
We have a normal UI in place to add your message and a send button to call the server methods.
Let's understand the code above part by part.
var chat = $.connection.letsChatHub;
Here, we set the connection to the Hub class. As you notice here, letsChatHub is the same hub class file name which we added to set up the server. The convention is that the method or class name starts with a lowercase letter. From here, we use chat to access the Send method.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Calls the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val(), finalConnId);
chat.server.send is self-explanatory. It sets the chat connection to call the server Send method once the connection is set and started.
chat.client.appendNewMessage = function (name, message) {
//
}
This is called when the Server receives the request and calls back the method on the client side.
How the Sample Would Work
The sample provided for download has a few instructions to follow:
- When you navigate to the LetsChat/LetsChat route, an alert pops up asking you for the name with which you would like to chat.
- Once you enter the name, you see a textbox which asks you for “Your friend’s connection Id”, but since you are starting the chat, you just send a message, and another pop up comes up, with your connection ID, which you need to share with your friends to share your message. Remember, only those who have your ID and use it while chatting will be able to see and send messages to you.
- When you friend navigates, he generates his connection ID and shares it with you in order to set up your connection completely. Thus, you need to have the connID to whom you will be sending and vice-versa with your friend as well. Then just chat!
If you would like to send a message to all and make that common, then use the Clients.All code snippet to send all.
Another interesting thing I figured out is the use of @section scripts{} that lets the SignalR scripts render on your page. Using the @section scripts provides your code with good style.
Share and Send Files Using SignalR?
Oops! Nice question, right?
It is ideally not advised. I would not recommend to send or share files using SignalR. There is always a better way to accomplish that. The idea would be using an API, you can have an upload area, and use SignalR to show the progress. Once the upload completes, update the user regarding the completion and generate a download link on the UI for the users to download and view the file.
This is not always the best idea, but is just another idea.
Conclusion
This is just a simple Chat application which you can use to chat with your friends if you host on Azure or any other domain. But again, SignalR is not limited. There are a lot of other effective uses of SignalR. I will be sharing more uses of SignalR in the next few articles.
Published at DZone with permission of Suraj Sahoo, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
A Complete Guide to AWS File Handling and How It Is Revolutionizing Cloud Storage
-
Integrating AWS With Salesforce Using Terraform
-
Structured Logging
-
Authorization: Get It Done Right, Get It Done Early
Comments