- •Contents at a Glance
- •Introduction
- •Who should read this book
- •Assumptions
- •Who should not read this book
- •Organization of this book
- •Finding your best starting point in this book
- •Conventions and features in this book
- •System requirements
- •Code samples
- •Notes on the version
- •Installing the code samples
- •Using the code samples
- •Acknowledgments
- •Errata & book support
- •We want to hear from you
- •Stay in touch
- •HTTP operations
- •Polling: The answer?
- •Push: The server takes the initiative
- •WebSockets
- •Server-Sent Events (API Event Source)
- •Push today
- •The world needs more than just push
- •What does SignalR offer?
- •Two levels of abstraction
- •Supported platforms
- •OWIN and Katana: The new kids on the block
- •Installing SignalR
- •Implementation on the server side
- •Mapping and configuring persistent connections
- •Events of a persistent connection
- •Sending messages to clients
- •Asynchronous event processing
- •Connection groups
- •The OWIN startup class
- •Implementation on the client side
- •Initiating the connection by using the JavaScript client
- •Support for older browsers
- •Support for cross-domain connections
- •Sending messages
- •Receiving messages
- •Sending additional information to the server
- •Other events available at the client
- •Transport negotiation
- •Adjusting SignalR configuration parameters
- •Complete example: Tracking visitors
- •Project creation and setup
- •Implementation on the client side
- •Implementation on the server side
- •Server implementation
- •Hub registration and configuration
- •Creating hubs
- •Receiving messages
- •Sending messages to clients
- •Sending messages to specific users
- •State maintenance
- •Accessing information about the request context
- •Notification of connections and disconnections
- •Managing groups
- •Maintaining state at the server
- •Client implementation
- •JavaScript clients
- •Generating the proxy
- •Manual generation of JavaScript proxies
- •Establishing the connection
- •Sending messages to the server
- •Sending additional information
- •Receiving messages sent from the server
- •Logging
- •State maintenance
- •Implementing the client without a proxy
- •Complete example: Shared drawing board
- •Project creation and setup
- •Implementation on the client side
- •Implementation on the server side
- •Access from other threads
- •External access using persistent connections
- •Complete example: Monitoring connections at the server
- •Project creation and setup
- •Implementing the website
- •System for tracing requests (server side)
- •System for tracing requests (client side)
- •External access using hubs
- •Complete example: Progress bar
- •Project creation and setup
- •Implementation on the client side
- •Implementation on the server side
- •Multiplatform SignalR servers
- •SignalR hosting in non-web applications
- •SignalR hosting in platforms other than Windows
- •Multiplatform SignalR clients
- •Accessing services from .NET non-web clients
- •Consumption of services from other platforms
- •Growing pains
- •Scalability in SignalR
- •Scaling on backplanes
- •Windows Azure Service Bus
- •SQL Server
- •Redis
- •Custom backplanes
- •Improving performance in SignalR services
- •Server configuration
- •Monitoring performance
- •Authorization in SignalR
- •Access control in persistent connections
- •Access control in hubs
- •Client authentication
- •An extensible framework
- •Dependency Injection
- •Manual dependency injection
- •Releasing dependencies
- •Inversion of Control containers
- •Unit testing with SignalR
- •Unit testing of hubs
- •Unit testing persistent connections
- •Intercepting messages in hubs
- •Integration with other frameworks
- •Knockout
- •AngularJS
- •Index
- •About the author
be different depending on the type of application: a multiuser shooter does not have the same requirements as a chat room service.
■■Persistence and non-volatility Normally, it is important for our software to ensure these aspects of the information.
■■Scalability For state storage solution, if the number of users grows exponentially, it might be necessary to distribute this very component among several physical nodes. If we cannot scale out the persistence mechanism, the scalability of our system will be irredeemably limited to it.
■■Security This component must offer features to protect the information proportional to the importance of the state data to be stored.
■■Operation environment The solution selected must be technically compatible with this environment. Also, the availability of interfaces allowing its use with .NET applications must be considered.
■■Knowledge and experience This is especially important for the development and maintenance team, who will have to use it to implement state persistence and keep it operating throughout the system’s life.
Along with the preceding criteria, of course, we must include other aspects of the product, such as its reliability, integrity, maintainability, community, support, licensing model, and so on.
Now that we have taken a first look at operation environments distributed across several nodes, this is a good time to anticipate a very important point that we delve into in Chapter 8, “Deploying and scaling SignalR.”
In distributed scenarios such as the one described previously, SignalR creates a challenge with no precedent in more traditional web applications: how to get the messages to reach their recipients. Something so relatively simple as sending a broadcast message to all users connected to one same point stops being trivial when there are several nodes to which a balancer has been assigning the users. For now, we will just reflect upon this; in Chapter 8, we will describe the problem in depth and we will go over the solution that SignalR offers for these situations.
Client implementation
The SignalR framework offers specific client libraries for the different types of systems that can consume services in real time: JavaScript, generic .NET applications, Windows Phone, and so on. They are all similar, yet obviously adapted to the peculiarities of their respective execution environments.
In Chapter 7, “Real-time multiplatform applications,” we will see other examples of use, but for now, we will continue to focus on the JavaScript client because it is easy to implement and very useful for a natural environment for this type of system: the web.
78 Chapter 5 Hubs
www.it-ebooks.info
JavaScript clients
There are two different ways of working with SignalR using JavaScript: with or without an automatic proxy. We have already anticipated many aspects of the first way in earlier sections, and it is really the most spectacular and easy to use because SignalR, on its own, takes care of a large portion of the work needed to achieve bidirectional communication with the server. Contrariwise, if we choose not to use an automatic proxy, we will have to make a little more effort to reach the same goals, because we will use a syntax that is more generic and aseptic, quite similar to the one used in other types of clients, as shown in the following example:
//With automatic/dynamic proxy: proxy.server.alert("Here I am");
//Without automatic proxy: proxy.invoke("alert", "Here I am");
We will continue studying the JavaScript client using the dynamic proxy, although later on we will also see some examples of direct use of the API without this aid.
Generating the proxy
To initiate the connection to the hub by using the JavaScript client with a dynamic proxy, we must reference the following script libraries from the page3:
<script src="/scripts/jquery-1.6.4.min.js"></script> <script src="/scripts/jquery.signalR-2.0.0.min.js"></script> <script src="/signalr/js"></script>
Note It is important to take into account the root path where the application is published when passed during production. For this reason, unless we are implementing the client on a pure HTML page, it is a good idea to use the methods provided by the different technologies to generate the URL based on virtual addresses of the resources, relative to the root of the site:
//In ASP.NET MVC 4 or above <script src="~/signalr/js"></script>
//In ASP.NET MVC 3
<script src="@Url.Content("~/signalr/js")"></script>
// WebForms
<script src="<%: ResolveClientUrl("~/signalr/hubs")%>"></script>
The first two scripts included, already used in the implementation of clients using persistent connections, are basic. SignalR’s client library for JavaScript (jquery.signalR) is a plug-in for JQuery, and
3 Remember that the version numbers of the script files referenced from the code can vary depending on the updates that you have installed in your project.
Hubs Chapter 5 |
79 |
www.it-ebooks.info
for this reason, both must be included in the page and in this order. Otherwise, a runtime script error will be generated, as shown in Figure 5-7, indicating the measures to be taken to solve it.
FIGURE 5-7 Error loading the main SignalR script in incorrect order.
Next we find a new reference to a script located at /Signalr/js. The first part of this path (“/Signalr”) is the address where the framework is expecting connections from the clients wanting to consume the services provided by the hubs. This path is common to all hubs in the application, and as we have seen, it can be easily modified in the call to the MapSignalR() method that the server executes during application startup, although normally the one provided by default will be valid and we will not need to change it.
The second part (“/js”) indicates the resource to be downloaded, in this case the proxy components that will allow easy access to the methods exposed by the different hubs present at the server.
Note In previous versions of SignalR, only the URL “/Signalr/Hubs” was used to download the proxies, but since version 2.0, “/Signalr/Js” is also allowed because this path is more appropriate for the type of resource that we want to obtain.
When the first request to this URL is received, SignalR will analyze the classes of the application inheriting from Hub and will create a script dynamically. Inside it, we will find a JavaScript object for every hub implemented at the server, which will act as a proxy of it on the client side. This process will take place only once, remaining stored in the memory for the following connections. The result generated—the JavaScript file that will be included in the page by the web client, as shown in
Figure 5-8—is sent to the client as it is.
FIGURE 5-8 Appearance of the JavaScript file generated.
80 Chapter 5 Hubs
www.it-ebooks.info
SignalR does not feature “out-of-the-box” implementation of the minimization of the JavaScript code—that is, the removal of all unnecessary characters from the file with the aim of optimizing its download. However, it has been taken into account internally, so we can use the marvelous extensibility features of the product to insert a minimization component in the middle of the process and achieve this goal. We will see how in due time.
Manual generation of JavaScript proxies
Sometimes it can be useful to have the JavaScript proxies in the form of a static file included in the site instead of having to generate it on the fly. This could make things easier for us at design time, because we would be able to benefit from IntelliSense or optimize downloads sending it to a CDN, making a more efficient use of the cache, or compacting and packaging the resulting script in the bundles used by the application.
For this, SignalR comes with a command-line application downloadable via NuGet that we can execute either manually or as part of the build process of the solution. We can obtain the tool, called SignalR.exe, by installing the following package:
PM> Install-Package Microsoft.AspNet.SignalR.Utils
This command will download and install the executable file in our solution, leaving it ready to be used from the package manager console itself; see Figure 5-9.
FIGURE 5-9 Execution of SignalR.exe from the package manager console.
To generate the JavaScript file of a project, we just enter the following command:
PM> signalr ghp /path:[your-hubs-dll-folder] /o:[output-file]
The path parameter indicates the path to the folder that contains the assembly in which the hubs for which we want to obtain the proxy are found. Normally, this will be the path to the /bin folder of the project. The o parameter indicates the full path and the name of the file to be generated.
Hubs Chapter 5 |
81 |
www.it-ebooks.info
In both cases, the path is relative to the current directory; the default NuGet console will be found in the root folder of the solution (we can query it with the command pwd), so we will be able to use relative paths to simplify the command, like this for example:
PM> signalr ghp /path:MyWebApp/bin /o:MyWebApp/scripts/hubs.js
We must take into account that the first time we execute this command, the file will be generated but it will not be included in the project, as shown in Figure 5-10. This is something that we have to do manually afterwards from the Visual Studio solution explorer, adding the existing file.
FIGURE 5-10 Proxy generated in the folder but not included in the project.
For better convenience, we can include this procedure as part of the building process of the application very easily. As shown in Figure 5-11, we just have to access the properties of the project and include the following command in the post-compilation command line:
$(SolutionDir)\packages\Microsoft.AspNet.SignalR.Utils.2.0.0\tools\signalr.exe ghp / path:$(TargetDir) /o:$(ProjectDir)/scripts/hubs.js
FIGURE 5-11 Automatic generation of the proxy after compiling.
Regardless of which way we generate the .js file, from now on it will obviously be the one that we reference from the pages where we use our hubs:
<script src="/scripts/hubs.js"></script>
82 Chapter 5 Hubs
www.it-ebooks.info