Created
January 14, 2014 16:53
-
-
Save XSockets/8421578 to your computer and use it in GitHub Desktop.
XSockets-Docs-3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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/YourChoice) #### | |
Platform Requirements Websockets WebRTC | |
Windows .NET 4+ Yes No | |
Other Mono Yes No | |
***Important:** You can actually connect any platform/device talking TCP/IP by creating a custom protocol. This will stil enable all client to talk to each other regardless of protocol since XSockets support cross-protocol communication.* | |
#### 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) | |
## 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. Install-Package XSockets | |
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. | |
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.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'); | |
}); | |
### 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'); | |
## 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 custom endpoints etc | |
### 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}); | |
The important thing to understand here is that you can pass any JSON 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}); | |
#### 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<T> | |
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<T> | |
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<T> | |
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<T>, SendToAllExceptMe<T> | |
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<T>, FindOn<T> | |
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. | |
#### 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) | |
{ | |
} | |
} | |
### 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 | |
TBD | |
#### Instanced Memory | |
TBD | |
## 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. | |
### 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. | |
### Configuration | |
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 default load assemblies (*.dll) and executables (*.exe) but you can set filters to be what you like. | |
### Exports & 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 | |
### Adding functionality at runtime (re-compose) | |
… | |
### DI/IoC - Using legacy architecture within XSockets | |
… | |
#### Ninject | |
… | |
#### Unity | |
… | |
#### XSockets.PluginFramework | |
… | |
### Customize (replace default functionality) with plugins | |
… | |
#### Write a custom JsonSerializer | |
… | |
### Add functionality) with plugins | |
… | |
#### Write a custom protocol | |
… | |
##### Connect NodeJS and Telnet to the custom protcol | |
… | |
##### Connect Arduino, NetDuino to the custom protcol | |
… | |
## 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 | |
### 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); | |
}); | |
### 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); | |
}); | |
#### 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); | |
}); | |
### 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); | |
}); | |
#### OnPeerConnectionCreated | |
Fires when the client has established a peer connectiond | |
rtc.bind(XSockets.WebRTC.Events.onPeerConnectionCreated, function(peer){ | |
console.log('OnPeerConnectionCreated',peer); | |
}); | |
#### OnPeerConnectionLost | |
Fires when a peer connection is lost (destroyed) | |
rtc.bind(XSockets.WebRTC.Events.onPeerConnectionLost, function(peer){ | |
console.log('OnPeerConnectionLost',peer); | |
}); | |
### Peer Methods | |
#### Remove Peer Connection | |
Lets you remove a connection from the current context. | |
rtc.removePeerConnection(peerId,callback); | |
#### Get Remote Peers | |
Get alist of peerId’s on the current context | |
rtc.getRemotePeers(); | |
### 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 | |
}); | |
#### 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); | |
}); | |
#### 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 | |
}); | |
## 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) | |
##### Constructor signatures of ConfigurationSetting | |
ConfigurationSetting(); | |
ConfigurationSetting(string location); | |
ConfigurationSetting(string location, ISet<string> origins); | |
ConfigurationSetting(string location, ISet<string> origins, int bufferSize, int numberOfAllowedConcurrentConnections); | |
ConfigurationSetting(string location, ISet<string> origins, string certificateSubjectDistinguishedName, StoreLocation certificateLocation); | |
ConfigurationSetting(string location, ISet<string> origins, int bufferSize, int numberOfAllowedConcurrentConnections, string certificateSubjectDistinguishedName, StoreLocation certificateLocation); | |
#### DNS Configuration | |
One of the most common questions about configuration is how to enable DNS configuration. For example, let's say that you want to connect to `ws://mysite.com:4502 ` the configuration for that could look like: | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using XSockets.Core.Common.Configuration; | |
using XSockets.Core.Configuration; | |
namespace XSockets.Test.Server | |
{ | |
public class SampleConfiguration: ConfigurationSetting | |
{ | |
static HashSet<string> GetOrigins(string origins) | |
{ | |
return new HashSet<string>(!string.IsNullOrEmpty(origins) ? origins.Split(',').ToList() : new List<string> { "*" }); | |
} | |
private IPEndPoint CreateIpEndpoint(Uri uri) | |
{ | |
IPAddress ipAddress; | |
IPEndPoint ipEndPoint = null; | |
if (IPAddress.TryParse(uri.Host, out ipAddress)) | |
ipEndPoint = new IPEndPoint(ipAddress, uri.Port); | |
else | |
{ | |
var addr = Dns.GetHostAddresses(uri.Host); | |
if (addr.Any(p => p.AddressFamily == AddressFamily.InterNetwork)) | |
ipEndPoint = new IPEndPoint(addr.First(p => p.AddressFamily == AddressFamily.InterNetwork), uri.Port); | |
} | |
return ipEndPoint; | |
} | |
public SampleConfiguration() | |
{ | |
// Set up the listener using the servers public/private ip depending on external setup | |
var endPoint = CreateIpEndpoint(new Uri("ws://192.168.1.7:4502")); | |
this.Endpoint = endPoint; | |
// Replace with your "dnsname" , this is used by handshake etc. if you connect using js , use this URL.... | |
// To test just add your machine IP to point to mysite.com in the host file on your machine | |
var uri = GetUri("ws://mysite.com:4502"); | |
this.Port = uri.Port; | |
this.Origin = GetOrigins("https://*,http://*"); | |
this.Location = uri.Host; | |
this.Scheme = uri.Scheme; | |
this.Uri = uri; | |
this.BufferSize = 8192; | |
} | |
} | |
} | |
#### Simplified DNS configuration in next version | |
Same as above will then be: | |
public class SampleConfiguration : ConfigurationSetting | |
{ | |
public SampleConfiguration() | |
{ | |
// Set up public/private ip depending on external setup | |
this.SetEndpoint("ws://192.168.1.7:4502"); | |
// Replace with your "dnsname" | |
this.SetDNS("ws://mysite.com:4502"); | |
} | |
} | |
*Note: To test just add your machine IP to point to mysite.com in the host file on your machine* | |
## 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 | |
### ConsoleApplication | |
… | |
### Windows Service | |
… | |
### Mono | |
… | |
### Raspberry PI | |
… | |
### The Cloud | |
... | |
#### Azure | |
… | |
#### Amazon | |
… | |
## Known Issues | |
### Plugin Framework | |
#### Cant add plugin filters at runtime | |
You should be able to add plugin filters at runtime by using the method `AddPluginFilter(string filter)` | |
**The work around** to add for example discovery in *.exe files is to add the setting into the .config file as shown below. | |
<appSettings> | |
<add key="XSockets.PluginCatalog" value="" /> | |
<add key="XSockets.PluginFilter" value="*.dll,*.exe" /> | |
</appSettings> | |
The issue will be fixed in 1.2 of the plugin framework. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment