Skip to content

Instantly share code, notes, and snippets.

@XSockets
Last active December 30, 2015 13:59
Show Gist options
  • Save XSockets/7838974 to your computer and use it in GitHub Desktop.
Save XSockets/7838974 to your computer and use it in GitHub Desktop.
XSockets-3.*-Docs
# XSockets 3.* Docs #
## Supported Platforms ##
### Server ###
XSockets runs pretty much anywhere and do note that the server ALWAYS deliver websocket-support. Feel free to compare us to alternative frameworks!
Platform Requirements Websockets WebRTC
Windows .NET 4+ Yes Yes
Other Mono Yes Yes
### Clients ###
#### Native (C#/Mono) ####
Platform Requirements Websockets WebRTC
Windows .NET 4+ Yes No
Other Mono Yes No
#### Browser (JavaScript)
Browser Websockets WebRTC Fallback
Chrome Yes Yes -
Safari Yes No -
Firefox Yes Yes -
Opera Yes Yes -
IE10+ Yes No -
IE6 - IE9 No No Longpolling (AJAX)
#### Other ####
Since XSockets supports implementation of custom protocols very easy you can actually connect from basically anything that have an ethernet connection. We have for example very easy connected Arduinos and NetDuinos to XSockets to talk cross protocol with browsers and other clients.
Platform Requirements
Any TCP/IP
## Installing XSockets.NET
XSockets.NET is distributed through [nuget.org](http://nuget.org) and [chocolatey.org](http://chocolatey.org).
From Chocolatey you install our Windows Service and on Nuget we have all our packages for development. You can read more about all the packages after the installation samples.
### Install into a WebApplication
1. Open up the Package Manager Console and install the server
PM> Install-Package XSockets.Server
2. Choose Add-Item (ctrl+shift+a)
3. Select XSockets.Web.Bootstrapper (suggested to add under App_Start)
4. Done
#### Start the server
The bootstrapper created above will only start when a server-side resource is requested. So if you are using a *.html file as client you will either
1. Have to request a server-side resource to kickstart it
2. or… Go to ProjectSettings in Visual Studio and set the Web to start on Development-Server instead of IIS-Express
Just hit F5 and the bootstrapper will start the XSockets server.
_Note: We do not recomend to host XSockets inside of IIS in test/production due to the fact that IIS recycles._
### Install into a Self-Hosted application
In the nuget Package Manager Console run the command below to install the server.
PM> Install-Package XSockets.Server
#### Start the server
Inside of the Main method start the server with the code below.
using XSockets.Core.Common.Socket;
using (var server = XSockets.Plugin.Framework.Composable.GetExport<IXSocketServerContainer>())
{
server.StartServers();
Console.WriteLine("Started, hit enter to quit");
Console.ReadLine();
server.StopServers();
}
### Chocolatey Packages
#### XSockets.Windows.Service
You can view [this video](http://www.youtube.com/watch?v=vDYKdB9Vuos) on how to install our Windows Service from Chocolatey
### Nuget Packages
#### XSockets
This package will behave in different ways depending on where you install it.
If you do not know what to do, use this package.
This package will install ItemTemplates for XSockets.NET into Visual Studio.
* If installed into a WebApplication the package will install
* XSockets.Server
* XSockets.Core
* XSockets.Plugin.Framework
* XSockets.Fallback (if MVC and .NET 4.0 or if .NET 4.5)
* XSockets.Client
* If installed into a Non-WebApplication the package will install
* XSockets.Server
* XSockets.Core
* XSockets.Plugin.Framework
#### XSockets.Server
This package contains the functionality to start a XSockets server.
Dependencies: - XSockets.Core
#### XSockets.Core
This package contains the functionality to write custom real-time controllers. Install this package into a class-library if you want to develop custom server-side functionality
Dependencies:
- XSockets.Plugin.Framework
#### XSockets.IIS8.Host
This package contains the functionality to use XSockets with WebAPI-Controllers or MVC-Conrollers
Dependencies: - XSockets.Server
#### XSockets.Plugin.Framework
A package for building modular applications in a blink.
Dependencies: none
#### XSockets.Fallback
A fallback from websocket to AJAX longpolling through a ASP.NET MVC Controller on .NET 4.0 and WebAPI if installed to .NET 4.5+
Dependencies: - XSockets.Client
#### XSockets.Client
Provides a socket client for connecting to a XSockets.NET server. This will enable communication between C#/Mono and a XSockets server.
Dependencies: - XSockets.Core
#### XSockets.JsApi
Provides a JavaScript API for communication with text/binary messages to a XSockets server with a publish/subscribe pattern
Dependencies: none
#### XSockets.Sample.Stockticker
A sample project copied from the SignalR stockticker but rewritten for XSockets so that you can compare it easier with SignalR.
Dependencies: XSockets
#### XSockets.Sample.WebRTC
A sample project that will provide a sample of a multi-video chat within the browser.
Source code found at: [https://github.com/XSockets/WebRTC](https://github.com/XSockets/WebRTC)
Dependencies: XSockets
## JavaScript - Client API
### Installation
1. Install-Package XSockets.JsApi
2. Drag the reference to XSockets.latest.js into yout page(s)
### Connecting
Below we connect the the built-in controller “Generic” on a server started at localhost port 4502.
var conn;
conn = new XSockets.WebSocket('ws://localhost:4502/Generic');
_Note: The Generic controller is awesome to use if you only want to write JavaScript and no C#/VB.NET._
### Events
There are a few events that will be triggered by the JavaScript API.
#### Open
When the connection to the server is established and the handshake is completed the Open event will be fired.
conn.on(XSockets.Events.open,function (clientInfo) {
console.log('Open', clientInfo);
});
Using Chrome, the sample above would output something like:
Open Object {ClientGuid: "a87d7070a6f6470aae99eb8e9377e25a", StorageGuid: "dac384ba5f4842c59ccdc210c4387f2d", clientType: "RFC6455"}
#### OnError
If the XSockets server send an error the OnError event will be fired.
conn.on(XSockets.Events.onError, function (err) {
console.log('Error', err);
});
To trigger an error you can try to connect to a non existing controller. So replace “Generic” with something made up (Foo for example) in the connectionstring.
Doing so would fire OnError with a message like:
Error Object {CustomMessage: "The handler name was not found in loaded plugins", Type: "XSocketException", Message: ""}
#### Close
When the connection is closed by the server (or lost due to some other issue) the Close event will be fired.
conn.on(XSockets.Events.close, function () {
console.log('Closed');
});
#### Reconnecting
If you loose the connection to the server for some reason you may wanna reconnect automatically. You can do that like this (and in any other way you see fit).
<script src="Scripts/jquery.latest.js"></script>
<script src="Scripts/XSockets.latest.js"></script>
<script>
var conn;
$(function () {
connect();
});
var subscribeTo = function() {
//Subscribe to some events, only one here...
conn.on('foo', function (data) {
//Fired when foo is published to us
console.log('foo', data);
});
};
var connect = function () {
conn = new XSockets.WebSocket('ws://127.0.0.1:4502/Generic');
conn.onopen = function (clientInfo) {
console.log('OPEN', clientInfo);
subscribeTo();
};
conn.onclose = function () {
console.log('CLOSED, will reconnect in 10 seconds');
//Lost connection, try to reconnect in x seconds.
setTimeout(function () {
connect();
}, 10000);
};
conn.onerror = function (err) {
console.log('ERROR', err);
};
};
</script>
### Publish/Subscribe
The WebSocket protocol and it’s OnMessage event will not take you very far. You need something more useful, we think that publish/subscribe is very useful in real-time frameworks. Clients subscribe and publish to topics and the framework dispatches messages to **ONLY** the subscribers. A real-time framework should **NEVER** broadcast data.
#### Publish/Trigger
When publishing you provide a topic (string) as well as some JSON data.
conn.publish('foo', {text:'Hello Real-Time World'});
#### On/Bind/Subscribe
In it’s simplest form a subscription takes a topic and a function/callback that will be called/fired when the topic is published to the client.
conn.on('foo', function(data) {
console.log('subscription to foo fired with data = ', data);
});
If we use the publish/subscribe sample above we would get output like:
subscription to foo fired with data = Object {text: "Hello Real-Time World"}
#### Unbind/Unsubscribe
To stop listening for a topic you have to unsubscribe.
conn.unbind('foo');
#### One
Sometimes you may want to subscribe for a topic only one time and then unsubscribe. To avoid implementing this yourself you can use the “one” binding.
conn.one('foo', function(data) {
console.log('subscription to foo fired with data = ', data);
});
#### Many
If you want to subscribe for a topic between 2 - n times you can use “many”. The second parameter decide how many ties you want to receive a topic before unbinding.
conn.many('foo',4, function(data) {
console.log('subscription to foo fired with data = ', data);
});
#### Understanding subscription confirmations
In specific cases you want to be sure that you are subscribing before you publish to a topic. By “be sure” we mean that you want to know that the server has registered your subscription. Therefor you can get a confirmation callback to a subscription as a third parameter.
conn.on('foo', function (data) {
console.log('subscription to foo fired with data = ', data);
}, function (confirmation) {
console.log('subscription confirmed',confirmation);
conn.publish('foo', { text: 'Hello Real-Time World' });
});
Above the “publish” will not be done until the server confirms the binding for “foo”.
If we use the publish/subscribe with confirmation sample above we would get output like:
subscription confirmed Object {event: "foo"}
subscription to foo fired with data = Object {text: "Hello Real-Time World"}
### Server side storage
Sometimes you might wanna persist some stuff on the server. The JavaScript API provides CRUD behavior on server side memory and you can retrieve data even if you refresh the page and get a new connection. Each client has it’s own repository from the JavaScript API.
#### Set
conn.publish(XSockets.Events.storage.set, {
Key: "yourKey",
Value: {
Name: "John Doe",
Age: 40,
Likes: ["Beer", "Food", "Coffe"]
}
});
#### Get
To get data we first have to subscribe for the “storage.get” event combined with the key “yourKey”.
conn.subscribe(XSockets.Events.storage.get + "yourKey", function (message) {
console.log(message);
});
Then publish the on the same topic bound to above to get the data.
conn.publish(XSockets.Events.storage.get, {Key: 'yourKey'});
If the “Set” sample is run the code above would output:
Object {Key: "yourKey", Value: "{Name:John Doe,Age:40,Likes:[Beer,Food,Coffe]}"}
#### Remove
To remove a object stored on the server use:
conn.publish(XSockets.Events.storage.remove, {Key: 'yourKey'});
#### GetAll
Getting all objects stored for a client is done by first subscribing:
conn.on(XSockets.Events.storage.getAll, function (data) {
data.forEach(function (item) {
console.log(item);
});
});
And the publish the “getAll” topic.
conn.publish(XSockets.Events.storage.getAll, {});
If the “Set” sample above is run (and not removed this would output:
Object {Key: "yourKey", Value: "{Name:John Doe,Age:40,Likes:[Beer,Food,Coffe]}"}
### Pass parameters when connecting
You can pass in data to the controller using querystring parameters when connecting.
conn = new XSockets.WebSocket('ws://localhost:4502/MyController?myParam=parameterValue')
See Server API for info about how to extract the parameter in the controller.
### Set a property value on the C# controller
If you have a property on your controller named MyProp and the setter is public you can change the value from JavaScript by:
conn.publish('set_MyProp',{value:'New Value'});
### Get a property value from the C# controller
To get C# property values in JavaScript you need to subscribe to the get_PropertyName topic and then publish the get_PropertyName topic.
//subscribe
conn.on('get_MyProp',function(prop){console.log('Value Of MyProp',prop)});
//publish
conn.publish('get_MyProp');
## WebRTC
XSockets has WebRTC support and the source code for both JavaScript and C# controller can be found at [gihub.com/XSockets/WebRTC](https://github.com/XSockets/WebRTC) The JavaScript API for WebRTC is decumented below. The easiest way to get started is to install our [sample package for WebRTC from nuget](http://www.nuget.org/packages/xsockets.sample.webrtc)
Install the sample by just using the command
Install-Package XSockets.Sample.WebRTC
###Demo
We dropped a simple example on github.io (http://xsockets.github.io/WebRTC/) that gives you a very simple "video conference". The demo allows 1-n clients to connect and share MediaStreems.
http://xsockets.github.io/WebRTC/
###Pre-Req
In order to be able to use the XSockets.NET PeerBroker and the WebRTC JavaScript API's of ours. You need to install XSockets.NET into your application. Since you are going to have a web-application we recomend you to use MVC, but it is up to you.
Install XSockets.NET WebRTC sample by...
Open the Package Manager console and type the following command.
PM> Install-Package XSockets.Sample.WebRTC
###Testing WebRTC
When installation is completed just follow the steps in the readme file.
**NOTE: Remember to use Chrome!**
*To learn more about the WebRTC API, read the API-Guide below*
----------
###Documentation
Here follows a brief description of the JavaScript API.
####Create a PeerConnection
In order to create a PeerConnection (`XSockets.WebRTC`) you need a PeerBroker to broker connections.
var broker = new XSockets.WebSocket("ws://localhost:4502/MyCustomBroker");
broker.subscribe(XSockets.Events.open, function(brokerClient) {
console.log("Broker Connected and client Created", brokerClient);
// Create the PeerConnection ( XSockets.WebRTC object )
rtc = new XSockets.WebRTC(broker);
});
####Configuration (Customize)
By passing a custom *configuration* into the ctor of the XSockets.WebRTC(broker,configuration) you can easily modify the iceServers, sdpConstraints and streamConstraints and parameters. You can also provide a set of expressions (sdpExpressions) that will be abale to intercept the SDP messages.
#####default configuration
{
"iceServers": [{
"url": "stun:stun.l.google.com:19302"
}],
"sdpConstraints": {
"optional": [],
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": true
}
},
"streamConstraints": {
"mandatory": {},
"optional": []
},
"sdpExpressions": []
}
##### Example modified iceServers & streamConstraints
var rtc = new XSockets.WebRTC(broker, {
iceServers: [{
url: 'stun:404.idonotexist.net'
}],
streamConstraints: {
optional: [{
'bandwidth': 500
}]
}});
// Will give you the following result;
{
"iceServers": [{
"url": "stun:404.idonotexist.net"
}],
"sdpConstraints": {
"optional": [],
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": true
}
},
"streamConstraints": {
"optional": [{
"bandwidth": 500
}]
},
"sdpExpressions": []
}
#####sdpExpressions
This expression parses and modifies the sdp and limits the video bandwidth 256 kilobits per second.
...
expression:[
function (sdp) {
return sdp.replace(/a=mid:video\r\n/g,
'a=mid:video\r\nb=AS:256\r\n');
}]
Expressions are passed into the settings object.
####Context Events
#####OnContextCreated
This fires when you have a connection to the Broker controller
rtc.bind(XSockets.WebRTC.Events.onContextCreated, function(ctx){
console.log('OnContextCreated',ctx);
});
// or use
rtc.oncontextcreated = function(ctx) {
// do op
}
#####OnContextChange
This fires when something happens on the context. Someone joins or leaves! You will get a list of peers on the current context.
rtc.bind(XSockets.WebRTC.Events.onContextChange, function(ctx){
console.log('OnContextChange',ctx);
});
// or use
rtc.oncontextchange = function(ctx) {
// do op
});
####Context Methods
#####Change Context
Changes your context on the broker. Pass in the Id of the context to join!
rtc.changeContext(ctxId);
#####Leave Context
Leave the current context... Hang up on all other peers
rtc.leaveContext();
####Peer Events
#####OnPeerConnectionStarted
Fires when the client starts to negotiate with the server
rtc.bind(XSockets.WebRTC.Events.onPeerConnectionStarted, function(peer){
console.log('OnPeerConnectionStarted',peer);
});
// or use
rtc.onpeerconnectionstarted = function(peer){
});
#####OnPeerConnectionCreated
Fires when the client has established a peer connection
rtc.bind(XSockets.WebRTC.Events.onPeerConnectionCreated, function(peer){
console.log('OnPeerConnectionCreated',peer);
});
// or use
rtc.onpeerconnectioncreated = functction(peer){
// do op
});
#####OnPeerConnectionLost
Fires when a peer connection is lost (destroyed)
rtc.bind(XSockets.WebRTC.Events.onPeerConnectionLost, function(peer){
console.log('OnPeerConnectionLost',peer);
});
// or use
rtc.onpeerconnectionlost = function(peer){
});
####Peer Methods
#####Remove Peer Connection
Lets you remove a connection from the current context.
rtc.removePeerConnection(peerId,callback);
#####Get Remote Peers
Get a list of peerId's on the current context
rtc.getRemotePeers();
// returns an Array of PeerID's i.e ["d383b53bb29947b5b1f62903bbc64d82"]
####MediaStream Methods
##### getUserMedia(constrints,success,failure)
Attach a local media stream ( camera / audio ) to the PeerConnection by calling `.getUserMedia(constrints,success,failure)`
rtc.getUserMedia(rtc.userMediaConstraints.hd(true), function(result){
console.log("MediaStream using HD constrints and audio is added to the PeerConnection"
,result);
});
##### addMediaStream(mediaStream,callback)
If you want to a (external) media stream to the PeerConnection (local) call the `addMediaStream(mediaStream,callback)`
window.getUserMedia(rtc.userMediaConstraints.qvga(false), function (stream) {
// Add the MediaStream capured
rtc.addLocalStream(stream, function () {
console.log("Added yet another media stream...");
});
});
##### removeStream(streamId)
To remove a local media stream from the PeerConnection and all connected remote peerconnection call the .removeStream(streamID) method
rtc.removeStream(streamId, function(id) {
console.log("local stream removed", id);
});
##### refreshStreams(peerId,callback)
When a media stream is added by using the .getUserMedia or .addMediaStream event you need to call refreshStreams method to initialize a renegotiation.
rtc.refreshStreams(peerId, function (id) {
console.log("Streams refreshed and renegotiation is done..");
});
** to get a list of all remote peerconnections call the .`getRemotePeers()` method.
#####getLocalStreams()
To get a list of the peerconnection (clients ) media-streams call the `.getLocalStreams()` method
var myLocalStreams = rtc.getLocalStreams();
####MediaStream Events
##### onLocalStream(event)
When a media stream is attached to the PeerConnection using `getUserMedia` och `addMediaStream` the API fires the `onLocalStream(stream)` event.
rtc.bind(XSockets.WebRTC.Events.onLocalStream, function(stream) {
// attach the stream to your <video> element or create a new <video> as you can add multiple streams to a PeerConnection
});
// or use
rtc.onlocalstream = function(event){
// do op
});
##### onRemoteStream(event)
When a remote PeerConnection is connected the API fires the `onRemoteStream(event)` .
rtc.bind(XSockets.WebRTC.Events.onRemoteSteam, function(event) {
console.log(event);
// event: {
// PeerId: 'Guid' // Identity if the RemotePeerConnection,
// stream: MediaStream
//}
// Attach the remote stream to a <video> an exisiting <video> element
attachMediaStream(document.querySelector("#remoteVideo"), event.stream);
});
// or use
rtc.onremotestream = function(event){
// do op
});
##### onRemoteStreamLost
When a remote stream removes a stream (`.removeStream(mediaStreamId)`) the JavaScript API fires the `onRemoteStreamLost(streamId`) event
rtc.bind(XSockets.WebRTC.Events.onRemoteStreamLost, function(event) {
console.log("a remote peerconnection removed a stream", event);
// remove video element by using the event.StreamID property
});
// or use
rtc.onremotestreamlost = function(function) {
// do op
});
####DataChannels
DataChannels can be attached to a PeerConnection by using the following `XSockets.WebRTC.DataChannel` object. upon each dataChannel you can publish and subscribe to any topic. Subscriptions can be added, deleted and modified without changing the underlying connection (no need for renegotiation).
####Create a new Datachannel (RTCDataChannel)
var dc = new XSockets.WebRTC.DataChannel("chat");
// Note you will need to add your DataChannel by calling addDataChannel(dc)
rtc.addDataChannel(dc);
// any event binding (open,close) or subscriptions needs to be tone prior to adding the channel
####DataChannel events
When you created your DataChannel object you can attach event listeners for the following events (not to be mixed with subscriptions)
#####onopen(peerId,event)
Fires when a DataChannel is open (ready)
dc.onopen = function(peerId,event){
// peerId is the identity of the PeerConnection
// event is the RTCDataChannel (native) event
};
#####onclose(peerId,event)
Fires when a DataChannel is closed (by remote peer )
dc.onclose = function(peerId){
// peerId is the identity of the PeerConnection
// event is the RTCDataChannel (native) event
};
####DataChannel methods
As described shortly above you can take advantake of a simple publish/subscribe pattern unpon you DataChannel.
#####subscribe(topic,cb)
Create a subscription to a topic (*topic*). callback function will be invoked when the datachannel receives a message on the actual topic
// Where dc is your XSockets.WebRTC.DataChannel object instance
dc.subscribe("foo", function(message)
{
console.log("received a message on foo", message);
});
#####unsubscribe(topic, cb)
To remove (unsubscribe) a 'topic' pass the *topic* and an optional callback function that will be called when completed.
dc.unsubscribe("foo", function() {
console.log("i'm no longer subscribing to foo");
});
#####publish(topic,data, cb)
To send (publish) invoke the publish method using the specific topic, *data* is the payload. the optinal callback (cb) function woll be invoked after the payload is sent.
dc.publish("foo", { myMessage: 'Petter Northug did not get a medal?' }, function(data){
// the payload passed will be available here as well...
});
// if you attach an event listener for onpublish the topic & data will be passed forward
dc.onpublish = function(topic, data) {
// do op
};
####XSockets.AudioAnalyser
Not yet documented fully documented. The main purpose is to be able to detect of the current user is silent / speaking during a certain interval (ms)
// Simple example where you on the onlocalstream event attaches a analyser anf grabs the results
rtc.onlocalstream = function(stream) {
// Attach the local stream captured
attachMediaStream(document.querySelector("#localStream"), stream);
// Create a an AudioAnalyzer, this detect if the current user is speaking (each second)
var analyze = new XSockets.AudioAnalyser(stream, 1000);
analyze.onAnalysis = function (result) {
console.log(result);
if (result.IsSpeaking) {
$("#localStream").toggleClass("speaks").removeClass("silent");
}else {
$("#localStream").toggleClass("silent").removeClass("speaks");
}
// Lets notify/share others,
ws.publish("StreamInfo", { peerId: rtc.CurrentContext.PeerId,streamInfo: result});
};
}
## C# - Client API
The C# Client API is mainly used for three things
1. You want to build a native app and use realtime technology.
2. You wan to boost a legacy part of you system (ASP.NET MVC, WebForms, WCF, ASMX etc) to be a part of your realtime architecture.
3. Connect to XSockets from a Raspberry PI or similar stuff.
### Determine How To Use The External API?
The API has two approaches and depending on what you want to achieve you need to know which one to use. Below is a description of the two options and the pros/cons for each one.
Although none of the alternatives is hard to implement it is crucial to understand the difference.
#### ClientPool
The ClientPool is really just a helper class that wraps the XSocketClient functionality. The benefit with this helper is that you can reuse connection and that will get rid of the handshaking before you can send a message. The helpers main focus is to help you send messages with high speed in a stateless environment without being forced to create a new instance of XSocketClient every time.
#### XSocketClient
This class will provide you with a set of tools for communicating with the XSockets server in a similar way as our JavaScript API. You will be able to take advantage our publish/subscribe pattern and communicate in full duplex in both directions
### Installation
1. Install-Package XSockets.Client
### Connecting
The connectionstring is used in the same way here as in the JavaScript API. The second parameter is the origin. We set "*" since the server by default accepts connection from all origins.
var client = new XSocketClient("ws://127.0.0.1:4502/MyController", "*");
client.Open();
### Events
The C# client API has a publish/subscribe pattern just like the JavaScript API, and just as the JavaScript API there are a few built-in events that will be invoked if you listen to them
#### Open
Will be invoked when the socket is open and the handshake is completed.
var client = new XSocketClient("ws://127.0.0.1:4502/MyController", "*");
client.OnOpen += client_OnOpen;
client.Open();
______________________________________________________
static void client_OnOpen(object sender, EventArgs e)
{
}
#### Error
Get information about errors.
client.OnError += client_OnError;
______________________________________________________
static void client_OnError(object sender, TextArgs e)
{
}
#### Close
When the connection is closed.
client.OnClose += client_OnClose;
______________________________________________________
static void client_OnClose(object sender, EventArgs e)
{
}
### Publish/Subscribe
The WebSocket protocol and it’s OnMessage event will not take you very far. You need something more useful, we think that publish/subscribe is very useful in real-time frameworks. Clients subscribe and publish to topics and the framework dispatches messages to ONLY the subscribers. A real-time framework should NEVER broadcast data.
#### Bind
When the topic "foo" is published the OnFoo method will be invoked.
client.Bind("foo", OnFoo);
______________________________________________________
private static void OnFoo(ITextArgs textArgs)
{
}
#### Unbind
To stop a subscription use Unbind and pass in the topic.
client.UnBind("foo");
#### Send/Trigger
To send (publish) a message with a specific topic use the Send or Trigger methods. You can pass in anything that can be serialized to JSON.
client.Send(new {SomeProperty = "Some Value"}, "foo");
The Send/Trigger has several overloads
Send(IBinaryArgs payload);
Send(ITextArgs payload, Action callback);
Send(object obj, string @event);
Send(string payload, Action callback);
Send(object obj, string @event, Action callback);
#### One
Sometimes you may want to subscribe for a topic only one time and then unsubscribe. To avoid implementing this yourself you can use the “one” binding.
client.One("FooOnce",OnFooOnce);
______________________________________________________
private static void OnFooOnce(ITextArgs textArgs)
{
}
#### Many
If you want to subscribe for a topic between 2 - n times you can use “many”. The second parameter decide how many ties you want to receive a topic before unbinding.
client.Many("Foo3Times",3,OnFoo3Times);
______________________________________________________
private static void OnFoo3Times(ITextArgs textArgs)
{
}
#### Understanding subscription confirmations
In specific cases you want to be sure that you are subscribing before you publish to a topic. By “be sure” we mean that you want to know that the server has registered your subscription. Therefor you can get a confirmation callback to a subscription as a third parameter.
To make the code easier to read I will create methods for the callbacks.
var socket = new XSocketClient("ws://127.0.0.1:4502/MyController", "*");
socket.OnOpen += SocketOnOnOpen;
socket.Open();
//When the connection is open we bind with confirmation
private static void SocketOnOnOpen(object sender, EventArgs eventArgs)
{
Console.WriteLine("OPEN");
//When we get a confirmation that “foo” is bound we call OnFooConfirmed
//and when there is a “foo” message we call “OnFoo”
socket.Bind("foo", OnFoo, OnFooConfirmed);
}
private static void OnFooConfirmed(ITextArgs textArgs)
{
Console.WriteLine("Confirmed Foo");
//We send a message on the bound event “foo”
//this will instantly trigger the callback “OnFoo” below.
socket.Send(new { message = "Hello World" }.AsTextArgs("foo"));
}
private static void OnFoo(ITextArgs textArgs)
{
Console.WriteLine("Foo: " + textArgs.data);
}
#### Deserializing the ITextArgs into your model
Since the ITextArgs interface is a wrapper for the event and data you have sent/recieved you probably want to know how to deserialize this data into the your model.
##### Lets look at an example
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
//Create a Person object
var p = new Person {Name = "Uffe", Age = 36};
//Create a TextArgs object with the p (Person) object as data and "person" as event
var textargs = p.AsTextArgs("person");
//Display the event and the JSON representation of p (Person)
Console.WriteLine("Event: " + textargs.@event);
Console.WriteLine("Data: " + textargs.data);
//Deserialize the JSON-string into a Person
p = textargs.data.Deserialize<Person>();
//Display the data
Console.WriteLine("Name: " + p.Name);
Console.WriteLine("Age: " + p.Age);
##### The output
Event: person
Data: {"Name":"Uffe","Age":36}
Name: Uffe
Age: 36
If you for some reason would like to not involve the TextArgs object you can of course serialize/deserialize object and JSON by just doing this...
var json = new Person {Name = "Uffe", Age = 36}.Serialize();
Console.WriteLine("JSON: " + json);
var p = json.Deserialize<Person>();
Console.WriteLine("Name: " + p.Name);
Console.WriteLine("Age: " + p.Age);
*Note: The XSocketClient has a property Serializer that you can use to Serialize/Deserialize data.*
### ClientPool
The ClientPool is really just a helper class that wraps the XSocketClient functionality. The benefit with this helper is that you can reuse connection and that will get rid of the handshaking before you can send a message. The helpers main focus is to help you send messages with high speed in a stateless environment without being forced to create a new instance of XSocketClient every time.
#### Get a connection
The ClientPool will keep track of existing connections so just use "GetInstance" and the ClientPool will create a new connection or return the existing one for this connectionstring.
var socketPool = ClientPool.GetInstance("ws://127.0.0.1:4502/MyController", "*");
#### Publish a message
The ClientPool is simple and is only a publisher (not a subscriber). To publish a message just use.
socketPool.Send(new {SomeProperty="Some Value"},"Foo");
Just as the XSocketClient you can send any object that can be serialized to JSON.
## C# - Server API
The XSockets.NET server API is built upon our modular architecture and you can override functionality, add new functionality very easy!
### Important Namespaces
Namespace Description
XSockets.Core.XSocket.Helpers Provide access to all the core extension.
XSockets.Core.Common.Socket.Event.Interface ITextArgs among other interfaces
XSockets.Core.XSocket XSocketController
XSockets.Core.Common.Socket.Event.Attributes Contains for example the NoEvent attribute
XSockets.Core.Common.Socket.Event.Arguments Contains classes used in events (OnOpen etc)
XSockets.Core.Configuration Configuration classes when creating custom endpoints etc
### Visual Studio - Item Templates
...
### Custom controllers
You can come along way by just using the “Generic” controller used in the JavaScript API, but at some point you probably want to write custom server-side code. When that time comes your first plugin/module will probably be a custom real-time controller. You can think of these controllers as ASP.NET MVC controllers with a big difference. They can communicate in full-duplex and keep state for each client!
#### Implementing
Easiest way to create a new controller if you are new to this is to add a new item (ctrl + shift + a). Choose the XSocketsController template and click add.
This will give you a new realtime controller (below named MyController)
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
namespace SampleProject
{
public class MyController : XSocketController
{
}
}
You will now be able to connect to your controller but you will not be able to communicate just yet (keep on reading).
#### Handle text messages
You can do a lot of things in a controller, but the most basic thing would be to implement the same logic as our built-in “Generic” controller. So lets write an exact copy of the “Generic” controller.
using XSockets.Core.Common.Socket.Event.Interface;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
namespace SampleProject
{
public class MyController : XSocketController
{
public override void OnMessage(ITextArgs textArgs)
{
this.SendToAll(textArgs);
}
}
}
This is actually an exact copy of the Generic controller except the fact that our controller is named MyController. If you publish a topic but there is no actionmethod defined, then the OnMessage method will take over. If you do not override OnMessage nothing will be triggered if there is no actionmethod for the topic.
This contoller will know who’s subscribing to what and it will dispatch all incomming messages to the correct clients (and ONLY the correct clients).
#### Create actionmethods
By creating a public method in your controller you have created an ActionMethod. The name of the method will be the “topic” when you publish/subscribe.
##### Using ITextArgs
If you do not know (or do not care) what kind of message the action method should receive you can set ITextArgs as the parameter type (as shown in HandleTextMessages).
Below we define a custom actionmethod “Foo” that will send back the data to the caller.
public void Foo(ITextArgs textArgs)
{
this.Send(textArgs);
}
To publish to the actionmethod above from JavaScript you could write:
conn.publish('foo',{SomeString:'Demo', SomeNumber:123});
To publish to the actionmethod above from C# you could write:
conn.Send(new {SomeString="Demo", SomeNumber=123},"foo");
The **important** thing to understand here is that **you can pass any object** since the actionmethod dont care… It will all be ITextArgs for the actionmethod.
##### Strongly typed model binding
A more useful example is to have a model (Person for example) that we expect to receive in our actionmethod.
First of all the model:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
And not the actionmethod to pass it to:
public void Foo(Person person)
{
this.Send(person,"foo");
}
To publish to the actionmethod above from JavaScript you could write:
conn.publish('foo',{Name:'BrickTop',Age:67});
To publish to the actionmethod above from C# you could write:
conn.Send(new {Name="BrickTop", Age:67},"foo");
//or if you have a reference to the assmebly where Person is defined
conn.Send(new Person{Name="BrickTop", Age:67},"foo");
#### Handle binary messages
We can override OnMessage for ITextArgs and we can do it for IBinaryArgs as well.
public override void OnMessage(IBinaryArgs binaryArgs)
{
//Logic here...
}
So any binary message passed will end up in the method above (on the specific controller)
#### Pass metadata with your binary data
One of the biggest issues with the WebSocket protocol (RFC6455) is that we cant pass any metadata with our binary data. We will just get some bytes and we have no clue what it is or what we should do.
In XSockets you can pass any JSON object together with the binary data, and when it arrives you can extract the JSON data into a custom object so that you know what to do with the binary data.
Let’s say that we have a model for files with filename and description
public class FileData
{
public string FileName { get; set; }
public string Description { get; set; }
}
We also override the OnMessage method and in there we want to extract the fileinfo and separate it from the actual file.
public override void OnMessage(IBinaryArgs binaryArgs)
{
//Convert the binaryArgs to a combinedMessage
//Will contain both ITextArgs and IBinaryArgs
var bm = binaryArgs.AsCombinedMessage();
//Get the strongly typed object passed with the arraybuffer
var fileInfo = bm.Extract<FileData>();
//Get the file
var file = bm.BinaryArgs.data;
//Add logic for saving etc...
}
The JavaScript for publishing to the method above could be:
var myJson = new XSockets.Message("newFile", { FileName: 'demo.txt', Description: 'Some info'});
var binaryMessage = new XSockets.BinaryMessage(myJson, getFileAsBuffer(),
function (arrayBuffer) {
// Okey, the buffer is created and wrapped in out simple "subprotocol".... send it!
ws.send(arrayBuffer.buffer);
});
_Note: getFileAsBuffer not inluded in sample._
### The importance of having state!
Having state is crucial in real-time applications. State is what helps us knowing what clients to send to (the subscribers), and state will also help us finding/sending-to a subset of subscribers...
Why is this important?
First of all we should **NEVER** broadcast data... A client that is not subscribing for a topic should never get a message about it. Second. If you have 1000 subscribers, but you only want to target 5 of them you have to be able to do so. The other 995 clients should **NOT** get the message and it should be **EASY** for you to find these 5 client as a subset from the 1000 subscribers.
#### An example...
Lets say that you have a chat (yeah I know it's always a chat). You have a lot of clients in the chat so you have to filter where to send the messages. To keep things simple we only have Name, City, Status and Hobby as criterias in the chat. So that the client can say "I'm James. A single person in Boston and my hobby is JavaScript".
We will now know the city, status and hobby of all the clients. What you do with this knowledge is up to you, but lets say that we want to send a message to all people having the same profile as the sender. What would that look like?
public class MyChat : XSocketController
{
public string Name { get; set; }
public string City { get; set; }
public string Status { get; set; }
public string Hobby { get; set; }
public void OnChatMessage(string message)
{
this.SendTo(p => p.City == this.City && p.Status == this.Status && p.Hobby == this.Hobby
, this.Name + " - " + message, "onChatMessage");
}
}
Thats it... And it is easy to set the properties on the controller from javascript if you do not want to pass them in with the connection (or write a method that handles the get/set stuff).
Bulding a UI for the MyChat controller could in its simplest form look somethinglike:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<h3>The everybody is named 'Glen' in Gothenbourg chat</h3>
<select id="cities">
<option value="New York">New York</option>
<option value="Boston">Boston</option>
<option value="Phoenix">Phoenix</option>
</select><hr />
<select id="status">
<option value="Single">Single</option>
<option value="Relationship">Relationship</option>
</select><hr />
<select id="hobby">
<option value="JavaScript">JavaScript</option>
<option value="CSharp">CSharp</option>
</select><hr />
<input type="text" id="input-message" value="Goo LR" />
<button id="btn-send">send</button>
<div id="messages"></div>
</body>
<script src="Scripts/jquery-2.0.3.js"></script>
<script src="Scripts/XSockets.latest.js"></script>
<script>
var conn = null;
$(function () {
//Create a connection
conn = new XSockets.WebSocket('ws://localhost:4502/MyChat');
conn.onopen = function () {
//Connected, set the C# properties
conn.publish('set_City', { value: 'New York' });
conn.publish('set_Hobby', { value: 'JavaScript' });
conn.publish('set_Status', { value: 'Single' });
conn.publish('set_Name', { value: 'Glen' });
//Subscribe for onchatmessage, but I will only get messages within the same city, hobby & status
conn.on('onchatmessage', function (d) {
$('#messages').prepend($('<div>').text(d));
});
};
//When the button is clicked
$('#btn-send').on('click', function () {
conn.publish('onchatmessage', { message: $('#input-message').val() });
});
//When city is changed
$('#cities').on('change', function () {
conn.publish('set_City', { value: $(this).val() });
});
//When status is changed
$('#status').on('change', function () {
conn.publish('set_Status', { value: $(this).val() });
});
//When hobby is changed
$('#hobby').on('change', function () {
conn.publish('set_Hobby', { value: $(this).val() });
});
});
</script>
</html>
### Understanding the extension methods
XSockets has a few extension methods that helps you a lot when writing server side logic. Most of the extensions is about sending (since this is a framework for communication).
#### Send&lt;T&gt;
Send any serializable object back to the caller.
Method signatures:
Send<T>(this T socket, IBinaryArgs binaryArgs)
Send<T>(this T socket, ITextArgs textArgs)
Send<T>(this T socket, object obj, string eventname)
##### SendToAll&lt;T&gt;
Send any serializable object to all subscribers connected to the controller T
SendToAll<T>(this T socket, IBinaryArgs binaryArgs)
SendToAll<T>(this T socket, ITextArgs data)
SendToAll<T>(this T socket, object obj, string eventname)
SendToAll<T>(this T socket, string text, string eventname)
##### SendTo&lt;T&gt;
Send to a subset of subscribers filtered by a lambda expression
So if you for example have a controller Foo with the property city (string) and the property age (int) you can send to a subset of clients by `this.SendTo(p => p.city == "new york" && p.age > 21, someobject,"topic")`
A cool thing is that it is generic so that you can send to clients connected to another controller by using `this.SendTo<T>`
SendTo<T>(this T socket, Func<T, bool> expression, ITextArgs textArgs)
SendTo<T>(this T socket, IEnumerable<T> clients, ITextArgs textArgs)
SendTo<T>(this T socket, IList<T> clients, IBinaryArgs binaryArgs)
SendTo<T>(this T socket, Func<T, bool> expression, object obj, string eventname)
SendTo<T>(this T socket, IEnumerable<T> clients, object obj, string eventname)
##### SendToAllExcept&lt;T&gt;, SendToAllExceptMe&lt;T&gt;
Send to all subscribers except the caller
SendToAllExcept<T>(this T socket, IList<T> clients, ITextArgs textArgs)
SendToAllExcept<T>(this T socket, Func<T, bool> expression, ITextArgs textArgs)
SendToAllExcept<T>(this T socket, IList<T> clients, object obj, string eventname)
SendToAllExcept<T>(this T socket, Func<T, bool> expression, object obj, string eventname)
SendToAllExceptMe<T>(this T socket, ITextArgs textArgs)
SendToAllExceptMe<T>(this T socket, object obj, string eventname)
#### RouteTo
Send a message back into the XSockets pipeline to land on another controller and a specific action method.
RouteTo<T>(this T socket, ITextArgs textArgs)
RouteTo<T>(this T socket, object obj, string eventname)
RouteTo<T>(this T socket, IBinaryArgs binaryArgs)
RouteTo<T>(this T socket, byte[] obj, string eventname)
#### Find&lt;T&gt;, FindOn&lt;T&gt;
The find extension will help you to get a subset of the clients connected to a controller. You will get intellisense based on the properties defined on a controller.
So if you for example have a controller Foo with the property city (string) and the property age (int) you can find a subset of clients by `this.Find(p => p.city == "new york" && p.age > 21)`
A cool thing is that it is generic so that you can find clients connected to another controller by using `this.FindOn<T>`
IEnumerable<T> Find<T>(this T client)
IEnumerable<T> Find<T>(this T client, Func<T, bool> expression)
void Find<T>(this T socket, Func<T, bool> expression, Action<IEnumerable<T>> action)
IEnumerable<T> FindOn<T>(this IXSocketController client)
IEnumerable<T> FindOn<T>(this IXSocketController client, Func<T, bool> expression)
void FindOn<T>(this IXSocketController client, Func<T, bool> expression, Action<IEnumerable<T>> action)
### Get/Set properties from the client API
If you have a property with a public getter or setter you can access the getter/setter methods from the client API’s
public string MyProp {get;set;}
The property above can be retrieved and changed from the client API’s (both JavaScript and C#).
Example on how to set a new value from JavaScript
conn.publish('set_MyProp',{value:'NewValue'});
See the client API’s for more information.
### NoEvent Attribute
All methods and public getters/setters of properties will be accessible from the clients. To be able to have a public method (or property) and dont expose it as a actionmethod you just have to add the NoEvent attribute.
//Method
[NoEvent]
public void ImNotAnActionMethod()
{
}
//Property
[NoEvent]
public string MyProp {get;set;}
The NoEvent attribute is located in the namespace XSockets.Core.Common.Socket.Event.Attributes
### Using events
The server side API offer a few events to help you out.
#### OnOpen
Invoked when the client is connected and the handshake is completed.
//Hook up the event in the constructor.
public MyController()
{
this.OnOpen += MyController_OnOpen;
}
void MyController_OnOpen(object sender, OnClientConnectArgs e)
{
//The connection is open...
}
The OnClientConnectArgs class is located in the namespace XSockets.Core.Common.Socket.Event.Arguments
#### OnClose
Invoked when the client is disconnected.
//Hook up the event in the constructor.
public MyController()
{
this.OnClose += MyController_OnClose;
}
void MyController_OnClose(object sender, OnClientDisconnectArgs e)
{
//Connection was closed
}
#### OnReopen
If a client reconnects and have been connected earlier the OnReopen event is invoked.
//Hook up the event in the constructor.
public MyController()
{
this.OnReopen += MyController_OnReopen;
}
void MyController_OnReopen(object sender, OnClientConnectArgs e)
{
//Welcome back...
}
#### OnAuthenticationFailed
See Authentication section
### Persist messages for users ‘between’ connections
XSockets has functionality to store messages in memory for a short period of time (configurable) so that clients loosing the connection, switching page etc can receive messages when they reconnect.
This is done in 3 steps
1. When a client disconnects, tell XSockets to subscribe to certain topics that the client should be able to receive later.
2. When a message is sent, tell XSockets to queue the message for clients currently off-line.
3. When the client reconnects, tell XSockets to publish queued messages.
#### OfflineSubscribe
The first step, tell XSockets to subscribe for me while I am gone/off-line.
//OnClose - subscribe for messages for me
void MyController_OnClose(object sender, OnClientDisconnectArgs e)
{
this.OfflineSubscribe("foo");
}
_Note: Messages is stored for 30 sec by default. You can override it in the method above_
#### Queue
The second step, queue the message when you send it.
public override void OnMessage(ITextArgs textArgs)
{
this.SendToAllAndQueue(textArgs);
}
_Note: There is also an extension method that takes a lambda expression to store messages only for certain clients!_
#### OnlinePublish
The third step, when the user gets back online send the messages queued.
//When the client reconnect...
void MyController_OnReopen(object sender, OnClientConnectArgs e)
{
this.OnlinePublish();
}
### Getting parameters passed in the connectionstring
You can pass in parameters in the connectionstring (see client API’s JavaScript and C#)
To get a passed in parameter:
if (this.HasParameterKey("myParam"))
{
var parameterValue = this.GetParameter("myParam");
}
### Get cookies
To be able to access cookies on the server you need run XSockets on the same domain as the website (the origins have to match). As an example… If you run the web on [http://localhost/](http://localhost/) but connect to XSockets with ws://127.0.0.1 you will not be able to access cookies. However, if you connect to XSockets on ws://localhost you will get access to the cookies!
To extract cookie data use:
if (this.HasCookieKey("myCookieName"))
{
var cookieValue = this.GetCookie("myCookieName");
}
### Errorhandling
XSockets has extensionmethods for helping you with exceptions. If you catch an exception you can use SendError to notify the client about the exception. The OnError event will be invoked in the client when using SendError.
public void WillThrowException()
{
try
{
throw new Exception("Fake error");
}
catch (Exception ex)
{
this.SendError(ex, "This text is optional");
}
}
### Longrunning (internal) controllers
A longrunning controllers is controllers that you can’t connect to. They are supposed to handle long-running task. It may be some polling to a legacy database or another task that you want to run as “a background thread”. You can then send messages to other controllers that will dispatch messages to clients.
This longrunning controller does nothing of value, just showing the concept.
[XSocketMetadata("MyLongrunningController", Constants.GenericTextBufferSize, PluginRange.Internal)]
public class MyLongrunningController : XSocketController
{
//The controller to send data to when the timer is elapsed
private static readonly MyController MyController = new MyController();
//Timer
private static readonly Timer _timer;
static MyLongrunningController()
{
//Initialize timer...
_timer = new Timer(1000);
_timer.Elapsed += _timer_Elapsed;
_timer.Start();
}
static void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
//Do stuff
//...
//Send info to a controller if you want to
MyController.SomeMethod(new SomeData{SomeProperty=SomeValue});
}
}
### Creating a custom pipeline
XSockets has a built in pipeline and all messages pass through the pipeline on the way in and on the way out. The pipeline has an ExportAttribute (from the plugin framework) with the Rewritable property set to true. This allows you to override the pipeline with your own implementation.
public class MyPipeline : XSocketPipeline
{
public override void OnMessage(IXSocketController controller, ITextArgs e)
{
//Incomming message
base.OnMessage(controller, e);
}
public override ITextArgs OnSend(XSockets.Core.Common.Protocol.IXSocketProtocol protocol, ITextArgs e)
{
//Outgoing message
return base.OnSend(protocol, e);
}
}
_Note: Remeber that you will affect performance if adding time consuming task in the pipeline._
### Interceptors
Interceptors gives you more freedom than the custom pipeline. There can only be one pipeline but you can add how many interceptors you want to. Just remember that (just like in the pipeline) you will affect performance if adding time consuming task in the interceptors.
***Note: By default interceptors are not enabled (unless in the Windows Service) so you have to enable the when starting the server.***
//tell the server to use interceptors.
wss.StartServers(withInterceptors:true);
#### ConnectionInterceptor
Lets you intercept connects, disconnects, handshake completed, handshake invalid
public class MyConnectionInterceptor : IConnectionInterceptor
{
public void Connected(OnClientConnectArgs args)
{
}
public void Disconnected(OnClientDisconnectArgs args)
{
}
public void HandshakeCompleted(OnHandshakeCompleteArgs args)
{
}
public void HandshakeInvalid(OnHandshakeInvalidArgs args)
{
}
}
#### MessageInterceptor
Lets you intercept incoming and outgoing messages just like the XSocketPipeline. A big difference between the two of them is that the pipeline lets you manipulate the message while the interceptor only receives them.
public class MyMessageInterceptor : IMessageInterceptor
{
public void OnMessage(IXSocketController socket, IBinaryArgs binaryArgs)
{
}
public void OnMessage(IXSocketController socket, ITextArgs textArgs)
{
}
public void OnSend(IXSocketController socket, IBinaryArgs binaryArgs)
{
}
public void OnSend(IXSocketController socket, ITextArgs textArgs)
{
}
}
#### ErrorInterceptor
Lets you intercept/catch errors on the server.
public class MyErrorInterceptor : IErrorInterceptor
{
public void OnError(OnErrorArgs errorArgs)
{
}
public void OnError(IXSocketController socket, OnErrorArgs errorArgs)
{
}
}
### Custom Protocols
One of many unique features in XSockets is how easy it is to add custom protocols. Since the protocols (as most things in XSockets) is a plugin you can add them by just creating a class.
The cool part is that XSockets supports cross-protocol communication. That means that if you connect for example a Arduino that talks a very simple protocol it will still be able to communicate with clients connected to for example RFC6455 (the websocket protocol).
This is huge strength since you within minutes can connect devices/things to participate in full duplex real-time communication.
**Why are custom protocols & cross protocol communication important?**
WebSockets is not the only transport for real-time communication. You might wanna connect devices/things that do not have the capability to talk RFC6455. It may be a micro-controller or a raw socket client. Neither way you do not want to reinvent the wheel. You might also wanna implement your custom subprotocol such as http://wamp.ws/ and use that instead of our default RFC6455 protocol. The choice is yours!
#### Creating a basic custom protocol
Lets create a really simple protocol for text-based communication.
The protocol will separate topic and message by including ":" in the messages. If there is no ":" in the message the topic will be set to "OnMessage"
##### Step 1 - Create a protocol class
Choose add new item and then locate the "XSockets.Protocol" template. Lets name the class TextProtocol.
##### Step 2 - Transform incoming data
From step one you got a new protocol plugin. Now we override the method `OnIncomingTextFrame`. The method have to return a ITextArgs object. We just convert the incomming string to an ITextArgs by some basic logic. Since the TextBased protocol need to handle pub/sub we do that in the simple helper method called `ConvertToTextArgs`
public override ITextArgs OnIncomingTextFrame(List<byte> payload)
{
var data = Encoding.UTF8.GetString(payload.ToArray()).Replace("\r\n", string.Empty);
if (data.Length == 0) return null;
ITextArgs ta;
if (data.Contains(":"))
{
ta = ConvertToTextArgs(data);
}
else
{
ta = new TextArgs(data, "OnMessage");
}
return ta;
}
private ITextArgs ConvertToTextArgs(string s)
{
var topic = s.Split(':')[0].ToLower().Trim();
var content = s.Split(':')[1];
if (topic == "subscribe")
{
var subscription = new XSubscriptions { Event = content };
var json = this.Controller.JsonSerializer.SerializeToString(subscription);
return new TextArgs(json, Constants.Events.PubSub.Subscribe);
}
if (topic == "unsubscribe")
{
var subscription = new XSubscriptions { Event = content };
var json = this.Controller.JsonSerializer.SerializeToString(subscription);
return new TextArgs(json, Constants.Events.PubSub.Unsubscribe);
}
return new TextArgs(content, topic);
}
##### Step 3 - Transform outgoing data
Since the client probably do not want to display JSON we convert the ITextArgs to a simple string.
public override byte[] OnOutgoingTextFrame(ITextArgs args)
{
return Encoding.UTF8.GetBytes(args.@event + ":" + args.data + "\r\n");
}
##### Step 4 - Test the protocol
1. Open up Putty (download from http://www.putty.org/) and connect to XSockets. In my case I run on 127.0.0.1 and port 4502.
**Important: Choose raw as connection type** and hit the "open" button
2. The first thing you have to type in is the "handshake". In this case we will only need to pass in the name of the controller to connect to and the name of the protocol.
So type `Generic TextProtocol` and hit enter.
3. The server will answer with a host response by default saying `Welcome to TextProtocol`
4. If you now have another client listening for `OnMessage` you can just start typing and the message will arrive at the subscriber(s). If you do not have another client such as a browser etc just open another instance of putty and connect in the same way, then type in `subscribe:onmessage` and you will get data :)
#### A more advanced custom protocol
TBD
###Controls frames (Ping & Pong )
The WebSocket protocol specification defines Ping and Pong frames that can be used for keep-alive, heart-beats, network status probing, latency instrumentation, and so forth.
The XBaseSocket.ProtocolInstance object contains events that deals with **OnPing** and **OnPing** Events is bound to each unique protocol instance.
By invoking the ProtocolInstance.Ping(byte[]) you are able to send a control-frame (ping) to the client, the client will respond with an control frame that will fire of the OnPong event.
Currently it's not possible to send control frames using JavaScript as the API dont expose any methods that enables us to do so.
Below is an exampke that shows how from a XSockets.Controller create control frames and measure the total round-trip time.
public class MyCustomController : XSocketController
{
private long RoundtripTime { get; set; }
public MyCustomController()
{
this.OnOpen += MyCustomController_OnClientConnect;
}
private void MyCustomController_OnClientConnect(object sender, OnClientConnectArgs e)
{
// Create a new timer that fire (elapses) each 10 seconds
var timer = new Timer(new TimeSpan(0,0, 10).TotalMilliseconds);
var roundtripWatch = new Stopwatch();
timer.Elapsed += (o, args) =>
{
roundtripWatch.Start();
// Just pass this clients as a part of the controlframe
this.ProtocolInstance.Ping(System.Text.Encoding.UTF8.GetBytes(this.ClientGuid.ToString()));
};
this.ProtocolInstance.OnPong += (o, args) =>
{
// Look if the pong originates from the current client id?
if (Guid.Parse(System.Text.Encoding.UTF8.GetString(args.Data)) != this.ClientGuid)
{
// Ops, this controlframe is corrupt?
// Disconect this client, would be a great idea?
this.Close();
}
roundtripWatch.Stop();
this.RoundtripTime = roundtripWatch.Elapsed.Milliseconds;
// Send the 'roundtrip time back to the client'
this.Send(new { lastKnownRoundtripTime = RoundtripTime},"onCalculatedRoundtrip");
roundtripWatch.Reset();
};
timer.Start(); // start the timer
}
}
The controller will on an interval of 10 seconds (using timer) pass a control frame (ping) and when pong is received calculate a roundtrip time. The example passes back a message to client subscribing on the topic of 'onCalculatedRoundtrip'.
**Client code**
..
var ws = new XSockets.WebSocket("ws://127.0.0.1:4502/MyCustom");
ws.on("onCalculatedRoundtrip", function (r) {
console.log("measure round trip-time in milliseconds", r)
});
..
### Utilities
####In-Memory Storage
XSockets.NET offer some simple helpers for storing objects in-memory so that you can access stuff between clients (and connections) in an easy way. Notice that you as a developer are responsible for this memory. The framework will not remove anything you add, this is all up to you as a developer.
##### Global Memory
XSockets has an In-Memory repository where you can store objects that can be shared between all connected clients. The beauty of this repository is that it is generic with the signature `Repository<TK,T>`. This means that every unique combination of `TK,T` will create yet another repository but you do not have to care, just use it.
So the sample below will actually create two different repositories. Since the repository is static it will be created upon first usage.
Repository<Guid, User>.AddOrUpdate(client.StorageGuid, userObject);
Now we use the same repository class to create another repsository, this time with the signature `Repository<Guid, Client>`
Repository<Guid, Client>.AddOrUpdate(client.StorageGuid, clientObject);
The methods available on the `Repository<TK,T>`:
T AddOrUpdate(TK key, T entity);
bool Remove(TK key);
int Remove(Func<T, bool> f);
void RemoveAll();
IEnumerable<T> Find(Func<T, bool> f);
IDictionary<TK, T> FindWithKeys(Func<T, bool> f);
IDictionary<TK, T> GetAllWithKeys();
IEnumerable<T> GetAll();
T GetById(TK key);
KeyValuePair<TK, T> GetByIdWithKey(TK key);
bool ContainsKey(TK key);
*Note: We are working on converting this into a overidable plugin so that you can use your favorite provider as storage for the data.*
##### Instanced Memory
TBD
####ObserverPool
XSockets.NET has a generic observer pattern `XSocketsObserverPool<TK, T>` implemented. This pattern lets you add (subscribe) for actions that will fire when notify/completed is triggered.
Namespace: `XSockets.Core.Utility.Observables`
Example.
//Just a sample id.
var g = Guid.NewGuid();
//Setup a subscription and the callback for notify and complete
XSocketsObserverPool<Guid, Person>.Subscribe(g, p => Console.WriteLine("Notification: " + p.Name), p => Console.WriteLine("Complete: " + p.Name));
//Create a person
var person = new Person {Name = "BrickTop"};
//Publish a notification
XSocketsObserverPool<Guid,Person>.Notify(g,person);
person.Name = "Tommy Four Fingers";
//Publish another notification
XSocketsObserverPool<Guid,Person>.Notify(g,person);
person.Name = "Steve Holt";
//Publish complete (this will also remove the subscription)
XSocketsObserverPool<Guid, Person>.Completed(g, person);
//This would output
Notification: BrickTop
Notification: Tommy Four Fingers
Complete: Steve Holt
*Note: As of 4.0 you will be able to have multiple subscriptions for the same id*
## The Plugin Framework
The plugin framework is inspired by MEF (Managed Extensibility Framework). MEF is awesome, but we wrote our own plugin framework to be able to run everywhere and also to avoid any dependencies. We did not copy stuff from MEF that we do not need and we added some extra features the we thought would be nice to have.
### Quick Start
A very basic example based on a MEF sample that you can find at http://www.amazedsaint.com/2010/06/mef-or-managed-extensibility-framework.html
**First of all...**
Open up the Package Manager Console (Tools->Library Package Manager->Package Manager Console) below called PMC.
Install by typing `Install-Package XSockets.Plugin.Framework` into the PMC and hit enter.
*The plugin framework has no dependencies and can be used in any project without using the rest of XSockets.NET*
#### Interfaces
// An interface that will export all implementing classes as a plugin/module.
// Exports Animals
[Export(typeof(IAnimal))]
public interface IAnimal
{
void Eat();
}
// An interface that will export all implementing classes as a plugin/module.
// Exports Zoo and the Zoo will import all exported animals
[Export(typeof(IZoo))]
public interface IZoo
{
[ImportMany(typeof(IAnimal))]
IEnumerable<IAnimal> Animals { get; set; }
}
#### Classes Implementing Interfaces
//Concrete animal 1
public class Lion : IAnimal
{
public Lion()
{
Console.WriteLine("Grr.. Lion got created");
}
public void Eat()
{
Console.WriteLine("Grr.. Lion eating meat");
}
}
//Concrete animal 2
public class Rabbit : IAnimal
{
public Rabbit()
{
Console.WriteLine("Crrr.. Rabbit got created");
}
public void Eat()
{
Console.WriteLine("Crrr.. Rabbit eating carrot");
}
}
//Our Zoo
public class Zoo : IZoo
{
public IEnumerable<IAnimal> Animals { get; set; }
}
#### The Program
Note that the the interfaces decides how the classes should behave regarding Exports/Imports
class Program
{
static void Main(string[] args)
{
//Get a single instance of Zoo... We only expect one zoo to exist
var zoo = Composable.GetExport<IZoo>();
//List the animals found in the zoo
foreach (var animal in zoo.Animals)
animal.Eat();
Console.ReadLine();
}
}
The output from the program would be...
Grr.. Lion got created
Crrr.. Rabbit got created
Grr.. Lion eating meat
Crrr.. Rabbit eating carrot
### Understanding the concept
The concept of the plugin framework is all about exporting and importing interfaces. We believe that all you need at compile time is the knowledge of the interface. The framework should load all exported and imported types at startup (runtime).
By having that kind of architecture we get very decoupled systems. We can replace current functionality (or add new functionality) by just implementing an interface. We can even do so at runtime if we want to.
### Config
By default the plugin framework will search for plugins at the location where the executable (or eqvivalent) is running. The plugin will be loaded at first usage of the plugin framework, but you can add assemblies (or paths to search) at runtime.
The framework will by defualt load assemblies (*.dll) but you can also add your own filters to the configuration.
### Exports &amp; Imports
By setting the `Export` attribute on a interface or class you tell the framework that you want to export this. And not surprising you import stuff by using `ImportOne` or `ImportMany`.
### Attributes
The explain the concept easily we will create a few simple classes and decorate them.
TBD
#### ImportOne
#### ImportMany
#### ImportingConstructor
#### Export
By adding the `Export` attribute to an interface (or a class) the plugin framework will detect and use the instance as a module/plugin.
You always have to provide the type being exported when using the `Export` attribute, but there other setting as well
##### InstancePolicy
Sets the lifecycle for the export. Using `InstancePolicy.Shared` will create a singleton of the export and only one instance will live in the app-domain. The default lifecycle is `InstancePolicy.NewInstance` and you will not need to set this option.
##### Rewritable
By default the Rewritable option is set to `Rewritable.No`, but if you set it to `Rewritable.Yes` your module/plugin can be overridden if someone implements the interface on another class. That framework will then throw your version away and use the new module/plugin.
##### MetaDataExport
TBD
### Adding functionality at runtime (re-compose)
Sometimes you might want to add both new interfaces and plugins at runtime, below is a simple example of howto do so.
Lets say that we have an interface IAnimal located in a separate assembly (project)
/// <summary>
/// This interface is not exported and
/// will not be available in the XSockets.Plugin.Framework (by default)
///
/// All interfaces that you are planning on using have to be known a compile time
/// </summary>
public interface IAnimal
{
void Says();
}
In another assembly (not referenced) we have a class Lion that implements the IAnimal interface
/// <summary>
/// This class does not have to be know at compile time
/// It might be added later and added to the XSockets.Plugin.Framework
/// It can be used since it has an interface!
/// </summary>
public class Lion : IAnimal
{
public void Says() { Console.WriteLine("Grrr"); }
}
In our XSockets solution we only reference the project/assembly with the interface IAnimal.
This interface is not exported by default, so we tell the plugin framework (at runtime) to also use IAnimal as an export.
That is done by
Composable.RegisterExport<IAnimal>();
But the Lion class is unknown so we add the assembly with Lion
Composable.LoadAssembly(@"C:\temp\someassembly.dll");
Then we tell the plugin framework to "recompose" = satisfy all imports/exports
Composable.ReCompose();
We can now get all instances of IAnimal using GetExport<T> or GetExports<T>...
#### Two different approaches listed below!
##### Approach 1
Here the classes implementing IAnimal is known in the current solution so we do not have to load any assemblies.
//Lets make the plugin framework aware of IAnimal
Composable.RegisterExport<IAnimal>();
Composable.ReCompose();
foreach (var animal in Composable.GetExports<IAnimal>())
{
animal.Says();
}
##### Approach 2
Here there are implementations of IAnimal in a assembly not yet loaded.
Composable.RegisterExport<IAnimal>();
Composable.LoadAssembly(@"C:\temp\PluginDemo.dll");
//OR... load many assemblies from a folder and possibly subfolders
//Composable.AddLocation(@"c:\temp\", SearchOption.AllDirectories);
Composable.ReCompose();
foreach (var animal in Composable.GetExports<IAnimal>())
{
animal.Says();
}
### Customize (replace default functionality) with plugins
#### Write a custom JsonSerializer
## Configuration
By default the XSockets server will start with two endpoints on port 4502, localhost and the machines IP. For example my current machine would start the server at ws://127.0.0.1:4502 and ws:192.168.1.6:4502
### Custom configuration
You can of course customize where to start the server among other things. There are two ways of creating custom configuration.
The namespace for configuration is XSockets.Core.Configuration
1. Pass in configurations/settings to the StartServers method
2. Create one or more configuration classes that XSockets will implement at startup
#### Passing configuration as a parameter
Just create the configurations needed and pass them to StartServers
//List of IConfigurationSettings
var myCustomConfigs = new List<IConfigurationSetting>();
//Add one configuration
myCustomConfigs.Add(new ConfigurationSetting("ws://192.74.38.15:4502"));
using (var server = Composable.GetExport<IXSocketServerContainer>())
{
server.StartServers(configurationSettings:myCustomConfigs);
Console.WriteLine("Started, hit enter to quit");
Console.ReadLine();
server.StopServers();
}
Note: you can of course pass in several configuration.
#### Setting configuration as a plugin ####
Just inherit the ConfigurationSetting class and implement your configuration. XSockets will find and and use these custom configurations.
public class MyTestConfig : ConfigurationSetting
{
public MyTestConfig() : base("ws://195.74.38.15:4502")
{
}
}
Now there is no need to pass in anything to the StartServers method, it will find the configuration above. When you use this technique the server will not create the default configuration. If you want to have for example 127.0.0.1:4502 as a configuration you have to add that as a plugin as well.
server.StartServers();
#### What can I configure?
The StartServers method has a number of options, and then the ConfigurationSetting class itself has a number of options.
##### Method signature of StartServers
StartServers(bool useLoopback = true, bool withInterceptors = false, IList<IConfigurationSetting> configurationSettings = null)
**Note: Interceptors are by default disabled!*
#### DNS Configuration
One of the most common questions about configuration is how to enable DNS configuration.
##### Public Endpoint
Let's say that you want to connect (client <-> server) to `ws://chucknorris.com:4502` the configuration for that could look like
public class ChuckNorrisConfig : ConfigurationSetting
{
public ChuckNorrisConfig() : base(new Uri("ws://chucknorris.com:4502")) { }
}
**Note: This setup will create an endpoint based on the DNS provided. Note that port 4502 have to be open.
##### Public & Private Endpoint
Let's say that you want to connect (client <-> firewall <-> server) to `ws://chucknorris.com:4502`, but the public endpoint is represented by a firewall. Your firewall will then forward the connection to our servers private IP address (for example 192.168.1.7).
public class ChuckNorrisConfig : ConfigurationSetting
{
public ChuckNorrisConfig() : base(new Uri("ws://chucknorris.com:4502"),new Uri("ws://192.168.1.7:4510")) { }
}
**Note: This setup requires that you forward traffic in your firewall to 192.168.1.7:4510
#### SSL/TLS
To get WSS you have to set the endpoint to be ´wss´ instead of ws, and you will also specify you certificate. This can either be done by setting CertificateLocation and CertificateSubjectDistinguishedName (as in the sample) or load the certificate from disk.
//Sample 1 - Certificate from store
public class ChuckNorrisConfig : ConfigurationSetting
{
public ChuckNorrisConfig() : base(new Uri("wss://chucknorris.com:4502"))
{
this.CertificateLocation = StoreLocation.LocalMachine;
this.CertificateSubjectDistinguishedName = "cn=chucknorris.com";
}
}
//Sample 2 - X509Certificate2
public class ChuckNorrisConfig : ConfigurationSetting
{
public ChuckNorrisConfig() : base(new Uri("wss://chucknorris.com:4502"))
{
this.Certificate = new X509Certificate2("file.name", "password");
}
}
## Security
### Authentication (Google, Twitter, Forms)
#### Authorize Attribute
This attribute can be set on Controller or Method level. If set at controller level all action methods that do not have the `AllowAnonymous` attribute will require authentication.
The authorize attribute can take Roles and Users but if using that you will have to implement your own authentication by overriding `OnAuthorization(AuthorizeAttribute authorizeAttribute)`
#### Get FormsAuthentication Ticket
When you have custom authentication you can get the `FormsAuthenticationTicket` from this method.
var ticket = GetFormsAuthenticationTicket();
*Note: If you do not pass in a cookiename .ASPXAUTH will be used.*
#### AllowAnonymous Attribute
This attribute can be set on action methods and will then allow anonymous access.
#### OnAuthenticationFailed (event)
Will be invoked when ever the `OnAuthorization` method returns false. Do note that if you override the `OnAuthorization` method you have to implement this logic your self.
#### OnAuthorization
You can implement your custom authorization by overriding the OnAuthorization method.
public override bool OnAuthorization(AuthorizeAttribute authorizeAttribute)
{
//Do validation and return true/false
return true;
}
*Note: If you have a custom model for validation get the cookie and cast it yourself for accessing roles, username etc.*
### SSL/TLS
## Scaling [BETA]
XSockets can scale by setting up 'siblings'. Siblings is just regular clients talking another protocol. When a server receives a message it will send it to all the siblings and they will dispatch the message to the clients that should get it.
### Setting up a cluster
If you setup sibling and they are not running.. the server will try to connect when a message are about to be sent.
//Add 2 siblings
container.AddSibling("ws://127.0.0.1:4503");
container.AddSibling("ws://127.0.0.1:4504");
//Start the server at 127.0.0.1
container.StartServers();
Console.WriteLine("Server started, hit enter to quit");
foreach (var sibling in container.GetSiblings())
{
Console.WriteLine("Sibling: " + sibling);
}
*Note: You can remove/add siblings at runtime*
## Hosting
We see more and more people hosting a server in desktop applications, even though that was not our intention it seems to work nicely. We will not cover that option below though.
### ConsoleApplication
Create a new ConsoleApplication and install the package XSockets. This will outpur some sample code for getting the server started.
Install by: `Install-package XSockets`
using (var container = XSockets.Plugin.Framework.Composable.GetExport<IXSocketServerContainer>())
{
container.StartServers();
Console.WriteLine("Server started, hit enter to quit");
Console.ReadLine();
}
### Windows Service
Use the XSockets.Windows.Service package from nuget to install XSockets as a windows service.
You can then add you custom configurations, controller, interceptors etc into the location of the service and restart it to make it find you custom code.
Note that by default you will get the Generic controller as well as the WebRTC controller named "Broker" installed with the service.
Install by:
1. If not done install chocolatey (http://chocolatey.org/)
2. Open up the Command Prompt and type `cinst XSockets.Windows.Service`
### Mono
On Mono you can start the server as a normal exe from Mono or run it as a background thread (deamon).
### Raspberry PI
You will need soft weexy to make the PI compatible with Mono. Read more here: http://xsockets.net/blog/xsockets-261-released
### The Cloud
Running XSockets in the cloud is really just a matter of configuration.
#### Azure
You will have to have the Azure SDK installed.
1. Create a Windows Azure Worker Role
2. Open the Package Manager Console
3. Install the XSockets.Server using
PM -> Install-Package XSockets.Server
4. Add the nessessary <appSettings> to the app.config file of the Worker Role
<appSettings>
<add key="XSockets.PluginCatalog" value="." />
<add key="XSockets.PluginFilter" value="*.dll,*.exe" />
</appSettings>
5. Open the Property Page for the Worker Role. You will find it in the /Roles/ foler of your WorkerRole Project.
6. Add a TCP endpoint using the Endpoints Tab.
- Name the Endpoint i.e 'MyEndoint'
- Set the type to input
- Set the protocol, to TCP
- Define the Public & Private port that you want to use.
7. Add a configuration setting using the Settings tab, we need to define the EndPoint Uri here.
- Set a name on the setting i.e 'MyUri'
- Set the type to string
- Set the value to i.e ws://127.0.0.1:4510/ (this depends on your enviorment).
8. Add the following code the the OnStart() method of your WorkerRole (WorkerRole.cs)
var container = XSockets.Plugin.Framework.Composable.GetExport<IXSocketServerContainer>();
// Create a Custom Configuration based on what we have defined using property pages.
var myCustomConfig = new List<IConfigurationSetting>();
var config = new ConfigurationSetting(new Uri(RoleEnvironment.GetConfigurationSettingValue("MyUri")))
{
Endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints.FirstOrDefault(n => n.Key.Equals("MyEndpoint"))
.Value.IPEndpoint
};
myCustomConfig.Add(config);
container.StartServers(configurationSetting: myCustomConfig);
9. Compile and run
10. Try connect to the Generic Controller using the following piece of JavaScript Code
var ws = new XSockets.WebSocket("ws://127.0.0.1:4510/Generic");
### Using IIS8
You can use XSockets with MVC-Controllers or WebAPI-Controllers. This lets you use use the Microsoft WebSocket implementation in IIS8 but still use the power of XSockets as usual.
####Pre-requisites
The following server requirements
1. IIS 8+
2. WebSockets protocol enabled ( see http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-websocket-protocol-support)
3. ASP.NET MVC 4.5
####Install the XSockets.IIS8.Host package
Open the package-manager console
Type `Install-Package XSockets.IIS8.Host`
Note you will also need the XSockets.JsApi package. `Install-package XSockets.JsApi`
####Start XSockets
You can choose to start either the RelayServer or a regular XSocket server.
***HINT: Use the `XSocketsWebBootstrapper` template to start XSockets and use the prefered interface in the call to GetExport.***
It is really just a matter of what interface you choose to load...
`IXSocketsRelayContainer` or `IXSocketsServerContainer`.
#####Using only Microsoft endpoints
var container = Composable.GetExport<IXSocketsRelayContainer>();
container.Start();
#####Using Microsoft endpoint and XSockets endpoint
This will enable you to connect over WebAPI/MVC but also use xsockets as ususal connecting to `ws://127.0.0.1:4502/XSocketsControllerName` for example
var container = Composable.GetExport<IXSocketServerContainer>();
container.StartServers();
####Create a real-time Controller
You can choose from MVC or WebAPI.
####WebAPI
Your ASP.NET Controller must inherit from the `XSockets.IIS8.Host.XSocketsApiControllerBase<XSocketsController>` class as follows.
Example ( WebAPI Controller).
public class ChatController : XSocketsApiControllerBase<RealTimechat>
{
}
XSocketsController (Realtimechat)
public class RealTimeChat : XSocketController
{
public void ChatMessage(ChatMessage message)
{
//Send all connected client's subscribing to onMessage
this.SendToAll(message,"onMessage");
}
}
####MVC
Your ASP.NET Controller must inherit from the `XSockets.IIIS8.Host.XSocketsControllerBase<XSocketsController>` class as follows.
Example ( ASP.NET MVC Controller).
public class ChatController : XSocketsControllerBase<RealTimechat>
{
//
// GET: /Chat/
public ActionResult Index()
{
return View();
}
}
XSocketsController (Realtimechat)
public class RealTimeChat : XSocketController
{
public void ChatMessage(ChatMessage message)
{
//Send all connected client's subscribing to onMessage
this.SendToAll(message,"onMessage");
}
}
####Connecting to your Realtime controllers using JavaScript
Connecting to a XSockets.IIS8.Host controller hosted using the `XSocketsRelayContainer` differs from connecting to a XSockets.NET Controller hosted by the `XSocketsServiceContainer` instead you of connecting directly to the `XSocketController` you connect to your Realtime MVC/WebAPI controller`XSocketsControllerBase<T>`/`XSocketsApiControllerBase<T>` as follows.
//MVC - replace the port with the port for your webapplication
ws = new XSockets.WebSocket("ws://localhost:1148/Chat");
//WebAPI - replace the port with the port for your webapplication
ws = new XSockets.WebSocket("ws://localhost:1148/Api/Chat");
ws.onopen = function (connection) {
console.log("My Connection", connection);
};
*As you see above we connect to the ASP.NET MVC-controller/WebAPI-controller (Chat) instead of RealTimechat.*
####Connecting to your Realtime controllers using C# Client
Connecting to a XSockets.IIS8.Host controller hosted using the `XSocketsRelayContainer` differs from connecting to a XSockets.NET Controller hosted by the `XSocketsServiceContainer` instead you of connecting directly to the `XSocketController` you connect to your Realtime MVC/WebAPI controller`XSocketsControllerBase<T>`/`XSocketsApiControllerBase<T>` as follows.
//MVC - replace the port with the port for your webapplication
var client = new XSocketClient("ws://localhost:1148/Foo","*");
//WebAPI - replace the port with the port for your webapplication
var client = new XSocketClient("ws://localhost:1148/Api/Foo","*");
client.OnOpen += ((sender, eventArgs) =>
{
Console.WriteLine("OPEN");
});
client.Open();
Console.ReadLine();
*As you see above we connect to the ASP.NET MVC-controller/WebAPI-controller (Chat) instead of RealTimechat.*
## Roadmap version 4
Features & functions we are working on for the 4th generation of XSockets.NET
### Enterprise
We will offer a new library for enterprise solutions including...
#### Scaling
The cluster will leave beta and have improved functionality. Only servers that has subscribers for a specific topic will get the message. So it will work like a publish/subscribe pattern within the cluster.
#### Loadbalancing
Enables you to setup a single endpoint and have x servers behind it on one or several machines. The loadbalancer can be configured to distribute the connections in the way you see fit for your solution.
### Plugability
Almost everything in XSockets is a plugin today, but we will extend the functionality so that you can add your own modules for the interface handling InMemory-repository. This way you can persist data with your data-provider of choice.
### OWIN Hosting
We recently added IIS8 support but in 4.0 we will also provide Owin as a hosting option and actually recommend it over the IIS8 host.
### Multiplexing over controllers on one connection
Currently you connect to XSockets and always specify the controller to use. You will be able to do so in the future as well, but you can also choose to only connect to the server and then communicate over several controllers from the same connection. We added this in 4.0 since it has been a request for several years and we now had the opportunity to add such functionality.
A sample
//Connecting to the server and pass in controllers to use (you can add more later)
var conn = new XSockets.WebSocket('ws://127.0.0.1:4502',['foo','bar']);
//Subscribe to `a` topic on the `foo` controller
conn.foo.subscribe('a',function(d){...});
//Subscribe to `a` topic on the `bar` controller
conn.bar.subscribe('a',function(d){...});
//Publish topic `a` on the `foo` controller
conn.foo.publish('a',{say:'I could be any object'});
//Publish topic `a` on the `bar` controller
conn.bar.publish('a',{say:'I could be any object'});
Note: You can still use XSockets as usual
As soon as you communicate on a previously `unused` or `closed` controller the onopen event will fire for that controller.
### Client Support
We are adding support for several new clients and will now cover:
- .NET 2.0 ***new***
- .NET 3.5 ***new***
- .NET 4.0
- Mono
- .NET Micro Framework 4.2 ***new***
- .NET Micro Framework 4.3 ***new***
- Xamarin Android ***new***
- Xamarin iOS ***new***
- Windows Phone 8.1 ***new***
- JavaScript
**Note: Our clients always deliver full duplex communication regardless of platform on client and server.**
And you can ofcourse write your own clients and custom protocol since we as always support cross-protocol communication
### Development tools/improvements
#### VB.NET Templates
We will add templates for VB.NET since we get questions about that once in a while.
#### Performance counters
We will also add new templates so that you with ease can add performance counters to measure messages, connections etc. And they will ofcourse be plugins as everything else.
#### Streamline the Client API's
We will change the naming of the methods on the client API's to streamline the JavaScript API with the C# API. This will mean that we will have Publish, Subscribe, One, Many as methods and the previously Trigger, Send, Bind etc will be removed.
#### C# Client - Waiting for result
This is a requested feature from many of our usres. XSockets is all about publish/subscribe and you cant call the server-side method and wait for the result being sent back. In 4.0 we have added this possibility as an extensionmethod to teh C# client API.
It will look something like...
var ct = new CancellationTokenSource();
var t = c.PublishAndWaitFor<Person>("saveuser", new Person() { Name = "Steve", Age = 33 }, ct);
Console.WriteLine(t.Result.Name);
#### Improved Binary Support
XSockets have had binary support for years and you can even pass MetaData with you blobs event though the websocketprotocol does not support that, but you always hot the binary message to one (and only one) method on the controller and you had to override the OnMessage method for binary data to get the message and then implement your logic.
As of 4.0 you can send binary messages just as regular pub/sub in XSockets, so now any actionmethod on the controller can be targeted to handle binary data.
#### Strongly typed callbacks in C# client
As you may have noticed above we did not get the usual `ITextArgs` object back but instead a object `T` (in the example a Person).
This means that you can subscribe for a topic and also specify the type of object you expect. So the framework will serialize the message to the correct type.
Example:
Below we subscribe for the topic `foo` and when the action is fired we expect a person to be provided.
c.Subscribe<Person>("foo",(person) => {Console.WriteLine(person.Name + " : " + person.Age);});
[1]: http://www.amazedsaint.com/2010/06/mef-or-managed-extensibility-framework.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment