- •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
As we said earlier and as you can infer from the code, these variables must be defined in the hub’s proxy, inside the object stored in its state property. In this case, the name of the variable used at the server must match the one defined at the client exactly, including letter case.
The internal operation of this feature is based on the command pattern and is quite simple. Given the above code, where we saw the setting of two state properties and a call to a remote method at the client, the information sent in the client-server direction would be approximately as follows:
data = { "H":"alertservice", "M":"Alert",
"A":["I felt a great disturbance in the force"], "I":6,
"S":{
"UserName":"Obi Wan", "MsgId":7
}
}
As you can see, the message sent encapsulates both the specification of the invocation to be performed on the server side (“H”: hub, “M”: method, “A”: args) and all the state variables (“S”: state) that have been defined. It is therefore important to use them prudently to avoid excessive bandwidth consumption.
After processing the method at the server, the information returned to the client is more or less as follows:
{
"S": { "MsgId":8
},
"I":"6"
}
In this case, we see that the client is returned a structure that contains information about several aspects, including the new value of the state variables modified during server processing (note the increase of the MsgId variable). Thus, when the data packet reaches the client, the latter can extract the property values and modify them on its local copy, maintaining a consistent application state between calls.
Implementing the client without a proxy
There might be scenarios where using a proxy is inconvenient, such as applications with a large number of hubs or applications that expose a large number of operations to their clients. In such— perhaps somewhat extreme—cases, using a proxy might be too costly in terms of the bandwidth and processing capacity needed on both ends. Also, a hacker could very easily obtain information about the hubs and their actions, which constitutes a perfect x-ray of the server’s attack surface.
Hubs Chapter 5 |
93 |
www.it-ebooks.info
However, we are not required to use proxies. Yes, they simplify development, but SignalR offers an additional alternative to directly communicate with hubs in scenarios where we do not want to use
a proxy.
In this case, the API is in fact quite similar to the one we find in other clients such as the generic
.NET client, where we do not have this type of syntactical sweetener. We will now very quickly study its main features, because the concepts are identical to those explained until now, this being just a different syntax to achieve the same purposes.
Establishing the connection
To initiate a connection to a hub without using proxies, it will not be necessary to reference the
/SignalR/Hubs script or whichever one we have set as we were doing before. Here it will suffice to include jQuery and SignalR in the page:
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
Next we must obtain a reference to the connection to the hub server and open it, similar to what we have done in previous examples:
var connection = $.hubConnection(); connection.start()
.done(function () { // Code
});
The URL where it is assumed by default that SignalR will be available at the server is /Signalr, which is the one used in the default mapping. However, we can modify it by supplying it to the $.hubConnection() method as a parameter, as you can see in the following example:
//Client side
var connection = $.hubConnection("/realtime");
//Server side configuration(startup.cs): app.MapSignalR("/realtime", new HubConfiguration());
Finally, if we are not using proxies, the best thing to do is to disable them at the server, something that can be done by specifying it when the hub is configured:
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration()
{
EnableJavaScriptProxies = false
});
}
Of course, if we have configured the generation of the proxy as a static file, we must not forget to eliminate it too.
94 Chapter 5 Hubs
www.it-ebooks.info
Invoking server methods
Even if we are not using the automatically generated proxy, on the client side we will always need an object representing the hub on which we can work, allowing us to invoke the methods and receive the events of the server.
We can create one of these objects, which is but a proxy after all, based on the connection previously referenced, like this:
var connection = $.hubConnection();
var proxy = connection.createHubProxy("AlertService");
To perform an operation from the server side, we use the invoke() method directly on the proxy that we have created:
var connection = $.hubConnection();
var proxy = connection.createHubProxy("AlertService"); connection.start().done(function () {
proxy.invoke("Alert", "I felt a great disturbance in the force");
});
As you can guess, the first argument is the name of the method or action to be invoked on the server side. In this case, the name is not case-sensitive: we will get the same result if we invoke the methods “alert” and “ALERT”.
The second argument and the following ones are values that are going to be supplied to the action for its execution. They can be any type; the server will be in charge of converting them to those specified in the method signature.
As usual, the invoke() method implements the promise pattern, so we can take control when execution has ended, whether in success—at which point we can obtain the return values—or in an error:
proxy.invoke("divide", 100, prompt("Divisor?"))
.done(function(result) { alert(result);
})
.fail(function(err) { alert(err);
});
State maintenance
This programming model also offers the ability to maintain state in client variables that are accessible from the server. In the same way as we saw when using dynamic proxies, we can use the state property for this:
... |
// Code |
|
|
var |
proxy = connection.createHubProxy("AlertService"); |
||
proxy.state.MsgId = 1; |
// Property |
accessible from server |
|
proxy.state.UserName = "Obi Wan"; |
// Property |
accessible from server |
Hubs Chapter 5 |
95 |
www.it-ebooks.info