Client C Sharp - XSockets/XSockets.NET-4.0 GitHub Wiki


C-Sharp

The C# Client API has support for:

  • .NET 3.5, 4.0+ (this section)
  • iOS (MonoTouch) (this section)
  • Android (MonoDroid) (this section)
  • .NET MicroFramework 4.2, 4.3 (see specific section for NETMF)
  • Dart (see specific section)
  • Arduino (see specific section)

Client Setup

To get the client just get the latest package from http://nuget.org/packages/xsockets.client

The C# clients ALWAYS talk full-duplex/bi-directional communication, and just like the XSockets server this behavior has nothing to do with what OS or WebServer you are running.

How to establish a connection

Just like in the JavaScript client you can multiplex over several Controller on one connection. To get a connections just create an instance of the XSocketClient.

var conn = new XSocketClient("ws://localhost:4502", "http://localhost", "chat");
conn.Open();
  • The first parameter is the endpoint of the server
  • The second parameter is the origin of the client
  • The third parameter is params string[] for setting your controllers

Do note that the call to Open is synchron and will wait until the connection is open and the handshake is completed.

How to AutoReconnect

By default the client will not reconnect if the connection is closed, but by just calling the SetAutoReconnect() method, you can pass in a timeout for the reconnect to occur. By default the timeout is 5 seconds. If the connection is closed and autoreconnect is enabled the client will try to reconnect, if that reconnect fails the event OnAutoReconnectFailed will be invoked. This way you can implement some custom logic between attempts to reconnect.

A simple sample where out IXSocketClient is _client, we also have a failcounter and after 4 attempts we stop trying to reconnect.

//Using the default timeout
_client.SetAutoReconnect();
//If the reconnect fails...
_client.OnAutoReconnectFailed += (sender, eventArgs) =>
{
    Console.WriteLine("AutoReconnect Failed");
    fail++;
    if (fail > 4)
    {
        _client.AutoReconnect = false;
    }
}

How to configure the connection

How to use multiple Controllers

To multiplex over several controllers on one connection you just pass in the controllers to use.

Below we connect to Controllers one and two

var conn = new XSocketClient("ws://localhost:4502", "http://localhost", "one","two");

Do note that you do not have to specify all controllers in the creation of the client. As soon as you start using a controller in the client API you will get an instance of it.

So if there is no controller three defined in the connection you can still do:

conn.Controller("three").Invoke("somemethod");

How to specify query string parameters

You can add querystrings to the NameValueCollection named QueryString on the IXSocketClient

var conn = new XSocketClient("ws://localhost:4502","http://xsockets.net", "chat");
conn.QueryString.Add("color","blue");
conn.Open();

How to specify HTTP headers

var conn = new XSocketClient("ws://localhost:4502","http://xsockets.net", "chat");
conn.Headers.Add("headername", "headervalue");
conn.Open();

How to specify client credentials

Connection header
var conn = new XSocketClient("ws://localhost:4502","http://xsockets.net", "chat");
conn.Headers.Add("myauthtoken", /* the token data */);
conn.Open();

Then verify the token in a custom AuthenticationPipeline or in the OnOpen even of the specific controller

Cookie with Forms Authentication
var authTicket = new FormsAuthenticationTicket(1, "Uffe",DateTime.Now,DateTime.Now.AddMinutes(15),false, "batman|hulk");            
string encTicket = FormsAuthentication.Encrypt(authTicket);
var authcookie = new Cookie(FormsAuthentication.FormsCookieName, encTicket);
conn.Cookies.Add(authcookie);
Certificate
var conn = new XSocketClient("ws://localhost:4502", "http://xsockets.net", "chat");
conn.AddClientCertificate(new X509Certificate2("mycert.pfx"));
conn.Open();
Windows Authentication
TODO

RPC

How to define methods on the client that the server can call

You define the methods per Controller since one connection can communicate over several Controllers

Client

The example below creates a listener for ChatMessage and it expects the data to be a string.

conn.Controller("chat").On<string>("chatmessage", data => Console.WriteLine(data));

Server

The server code for the examples with the ChatModel can look like

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

this.InvokeToAll("I am Yogi the gummi bear","chatmessage");

Methods without parameters

If the method you're handling does not have parameters just use the non generic On method.

Server

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

public class Chat : XSocketController
{
    public void CallAllClients()
    {
        this.InvokeToAll("test");
    }
}

Note: You can of course use InvokeTo<T> or Invoke as well

Client

conn.Controller("chat").On("test", () => Console.WriteLine("Test Was Called"));

Methods with parameters, specifying parameter types

If we have a complex object being sent from the server, like the ChatModel we can of course just do like this

conn.Controller("chat").On<ChatModel>("chatmessage", data => Console.WriteLine(data.UserName + " - " + data.Text));

Server

The server code for the examples with the ChatModel can look like

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

this.InvokeToAll(new ChatModel{Name="Yogi", Text="I am a gummi bear"},"chatmessage");

Methods with parameters, specifying dynamic objects for the parameters

As an alternative to specifying specific types to the On method, you can specify parameters as dynamic objects.

conn.Controller("chat").On<dynamic>("chatmessage", data => Console.WriteLine(data.Text));

Note: Dynamic keyword will (currently) not exist in clients for Android/iOS. Note: Dynamic keyword does not exist in .NET 3.5 and earlier.

Server

The server code for the examples with the ChatModel can look like

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

this.InvokeToAll(new ChatModel{Name="Yogi", Text="I am a gummi bear"},"chatmessage");

Methods with parameter of type IMessage

If you for some reason want the actual IMessage sent from the server to the client you can specify IMessage as the type. You will then get the complete object that the client received!

conn.Controller("chat").On<IMessage>("chatmessage", o => Console.WriteLine("{0}, {1} {2}, {3},",o.Controller, o.Topic, o.Data, o.MessageType);

If you know the type contained in the Data part of the IMessage you can use Extract to get the value.

//example extracting the JSON into a specific type
var chatModel = o.Extract<ChatModel>();

Server

The server code for the examples with the ChatModel can look like

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

this.InvokeToAll(new ChatModel{Name="Yogi", Text="I am a gummi bear"},"chatmessage");

How to remove a handler

When you want to dispose of a handler you have to remove it from the Controller where it was used.

var listener = conn.Controller("chat").On<string>("chatmessage", data => Console.WriteLine(data));   
            
conn.Controller("chat").DisposeListener(listener);

How to send to server methods from the client

To call a method on the server, use the Invoke method on the Controller.

If the server method has no return value, use the non-generic overload of the Invoke method.

Server - method without return value

//using XSockets.Core.XSocket;

public class Chat : XSocketController
{
    protected string UserName {get;set;}
    public void SetUserName(string userName)
    {
        this.UserName = userName;
    }
}

Client - calling a method that has no return value

conn.Controller("chat").Invoke("setusername", new {userName = "Steve"});

How to call a server side method and wait for the result

If the server method has a return value, specify the return type as the generic type of the Invoke method.

Server - for a method that has a return value

public IEnumerable<Stock> GetStocks()
{
    return _stockTicker.GetAllStocks();
}

The Stock class used for return value

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Client - calling a method that has a return value in a synchronous method

var stocks = conn.Controller("stockticker").Invoke<IEnumerable<Stock>>("GetStocks");
foreach (Stock stock in stocks.Result)
{
    Console.WriteLine("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

If there is no respons for 30000 ms there will be a TimeoutException so you should wrap the synchronous call like:

try
{
    var stocks = conn.Controller("stockticker").Invoke<IEnumerable<Stock>>("GetStocks");
    foreach (Stock stock in stocks.Result)
    {
        Console.WriteLine("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
    }
}
catch (AggregateException ae)
{
    ae.Handle((x) =>
    {
        if (x is TimeoutException)
        {
            //The communication did not respond within given time frame
            //Handle it...
            return true;
        }
        //Another exception handle it... or return false to stop app
        return false;
    });
}

Of course you can set the default 30000 ms to be longer or shorter if needed. Just pass in your timeout as a parameter in the call like

//Timeout will now be 5 seconds
var stocks = conn.Controller("stockticker").Invoke<IEnumerable<Stock>>("GetStocks",5000);

PUB/SUB

How to define subscription methods on the client that the server can publish to

conn.Controller("stockticker").Subscribe<Stock>("newStock", stock => Console.WriteLine("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price));

How to subscribe one time

If you only want to get a message once and then unsubscribe automatically you can use one

conn.Controller("stockticker").One<Stock>("newStock", stock => Console.WriteLine("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price));

This will make sure that the client unsubscribe to the topic after getting the first message.

How to subscribe n times

If you want to get a message 1 to n times and then unsubscribe automatically you can use many

conn.Controller("stockticker").Many<Stock>("change", 3, data => Console.WriteLine(data));

This will unsubscribe the change topic after getting 3 messages.

How to know when the subscription is ready

When you compare PUB/SUB with RPC an obvious disadvantage with PUB/SUB is that you do not know if the server has bound the subscription when you do a publish. Therefor you can pass in an additional function to get a callback from the server when the subscription is completed.

This works similar for all subscribe methods.

Subscribe<T>(string topic, Action<T> callback, Action<IMessage> confirmCallback);
One<T>(string topic, Action<T> callback, Action<IMessage> confirmCallback);
Many<T>(string topic, uint limit, Action<T> callback, Action<IMessage> confirmCallback);

where callback is called when a publish occurs and confirmCallback is the callback that confirms the subscription.

How to remove a subscription

When you no longer want to subscribe to a topic you just use the unsubscribe method to tell the server to remove the subscription.

conn.Controller("chat").Unsubscribe("chatmessage");

How to publish to server methods from the client

Client

conn.Controller("chat").Publish("chatmessage",new {Text= "Hello people!"});

Server

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

//The server migth publish the message back to all clients subscribing
public void ChatMessage(ChatModel chatModel)
{
    this.PublishToAll(chatModel, "chatmessage");
}

How to handle connection lifetime events

The events on connection level provide information about the socket being opened/closed. The controllers has their own lifetime events.

OnConnected

conn.OnConnected += (sender, eventArgs) => Console.WriteLine("Connected");
conn.Open();

OnDisconnected

conn.OnDisconnected += (sender, eventArgs) => Console.WriteLine("Disconnected");
conn.Open();

How to handle controller lifetime events

The controller has nothing to do with the actual socket. These events tell you about the controllers you are using over your connection.

OnOpen

conn.Controller("chat").OnOpen += (sender, connectArgs) => {
    Console.WriteLine("Opened");
};

OnClose

conn.Controller("chat").OnClose += (sender, disconnectArgs) =>{
    Console.WriteLine("Closed");
};

Open a controller

As soon as you start to communicate over a new controller the OnOpen event will fire. You actually do not need to specify the controller in the connection. As long as the controller exists on the server the OnOpen event will fire.

Close a controller

To close as controller (not the actual connection/socket) you just call the Close method on the controller instance. This will fire the OnClose event on the controller.

conn.Controller("chat").Close();

OnOpen

conn.Controller("chat").OnOpen += (sender, connectArgs) => {
    Console.WriteLine("Open {0}", connectArgs.ClientInfo.Controller);
};

OnClose

conn.Controller("chat").OnClose += (sender, disconnectArgs) => {
    Console.WriteLine("Closed {0}", disconnectArgs.ClientInfo.Controller);
};

Binary data

XSockets has supported binary messages for a long time, but in 4.0 we have made it even easier than before.

How to handle binary data

Lets say that we have a file c:\temp\xfile.txt with the text This file was sent with XSockets.NET and we want to send that file to the server.

Client

Client - C#

var blob = File.ReadAllBytes(@"c:\temp\xfile.txt");
conn.Controller("chat").Invoke("myfile", blob);

Server

//using XSockets.Core.Common.Socket.Event.Interface;
//using XSockets.Core.XSocket;

public void MyFile(IMessage message)
{
    var filecontent = Encoding.UTF8.GetString(message.Blob.ToArray());
}

How to pass meta-data together with binary data

If we want to attach metadata about the binary data that is easy to do. Just pass along the object representing the metadata and XSockets will let you extract that data on the server.

Client

Client - C#

var blob = File.ReadAllBytes(@"c:\temp\xfile.txt");
conn.Controller("chat").Invoke("myfile", blob, new {Name="xfile.txt"});

Server

//using XSockets.Core.Common.Socket.Event.Interface;
//using XSockets.Core.XSocket;

//simple class for holding metadata about a file
public class FileInfo
{
    public string Name {get;set;}
}

public void MyFile(IMessage message)
{
    var filecontent = Encoding.UTF8.GetString(message.Blob.ToArray());
    var metadata = message.Extract<FileInfo>();
}

Just use Extract<T> to get back to metadata attached to the binary data.

How to handle errors

To handle errors that the client raises, you can add a handler for the Error event on the connection object.

conn.OnError += (sender, errorArgs) => Console.WriteLine(errorArgs.Exception.Message);

You may also handle errors on individual controller using the OnError event on a specific controller.

conn.Controller("chat").OnError += (sender, errorArgs) => Console.WriteLine(errorArgs.Exception.Message);

To handle errors from method invocations, wrap the code in a try-catch block.

//using XSockets.Core.XSocket;
//using XSockets.Core.XSocket.Helpers;

try
{
    var stocks = conn.Controller("stockticker").Invoke<IEnumerable<Stock>>("GetStocks");
    foreach (Stock stock in stocks.Result)
    {
        Console.WriteLine("Symbol: {0} price: {1}", stock.Symbol, stock.Price);
    }
}
catch (Exception ex)
{
    Console.WriteLine("Error invoking GetStocks: {0}", ex.Message);
}

Client Pool

The client pool lets you call methods on the server. The client pool is made for sending only and is often used in legacy applications to boost them to real-time with 2 lines of code.

For example, if you have a half-duplex service such as a WCF you can boost it to real-time by just doing this.

var conn = ClientPool.GetInstance("ws://localhost:4502","http://localhost");
conn.Send("Hello from client pool","chatmessage","chat");

Above we send the text Hello from client pool to the method ChatMessage on the controller Chat. As always you can send any object that can be serialized.

Using In-Memory Storage

This feature lets you store any serializable object on the server by using simple methods on the client API's. This is useful when you need to store data between connections/pages for the client. You are responsible for cleaning up the memory your self since data written to the storage will remain there until you remove it or the server stops.

When you use the storage you always work on a controller instance since each controller can have its own storage

Note: You can set any serializable object as value in the storage

Setting data on the server

Set the generic type that you want to store (in this case string)

conn.Controller("chat").StorageSet("color","blue");

Get notifications when the data changes on the server

Since the storage is per client you will only get notifications for changes in the current client.

conn.Controller("chat").StorageOnSet<string>("color", s => Console.WriteLine("Color {0} was set in storage", s));

Getting data from the server

var color = conn.Controller("chat").StorageGet<string>("color");
Console.WriteLine("Got color {0} from storage", color);

Removing data on the server

conn.Controller("chat").StorageRemove("color");

Get notifications when the data is removed on the server

conn.Controller("chat").StorageOnRemove<string>("color", s => Console.WriteLine("Color {0} was removed from storage", s));

Clear the storage

conn.Controller("chat").StorageClear();

⚠️ **GitHub.com Fallback** ⚠️