Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Implement a Websocket API with Owin and SignalR [Part 2]

4.60/5 (3 votes)
4 Dec 2017CPOL5 min read 15.9K   415  
Let's use the API we created in Part 1 to show the user some statistical graphs.

Introduction

We now have our data being pushed to us.

Lets make use of SignalR to push this information to the client and display it on a real time graph.

 

This article is the second part in the series.

Part 1 can be found here: Creating a Real-time Cryptocurrency Websocket API [Part 1].

Prerequisites

  • Visual Studio 2015 or 2017 with Framework 4.6.1
  • A Coinigy API key and secret (please see Part 1 on how to get this key and secret)

Table of Contents

  1. Using the code
    • Step 01 - Create a Web Application
    • Step 02 - Install Nuget packages
    • Step 03 - Create a Startup Class
    • Step 04 - Create a hub
    • Step 05 - Subscribe to events
    • Step 06 - Subscribe to Trade Channel
    • Step 07 - Connect and push trade messages to client
    • Step 08 - Create an HTML page
    • Step 09 - Create a JavaScript file
    • Step 10 - Subscribe and update charts
  2. Final words

Using the Code

Step 01 - Create a Web Application

Open Visual Studio and go to File > New > Project and select ASP.NET Web Application.

Ensure your framework targets .NET Framework 4.6.1.

Give your project a name (e.g. Coinigy.Web) and click "OK".

Select the "Empty"-template and ensure nothing else is checked or selected.

Click "OK".

Step 02 - Install Nuget Packages

In the package manager console, install the following packages using these commands:

"Install-Package PureSocketCluster"

"Install-Package Microsoft.AspNet.SignalR"

Step 03 - Create a Startup Class

We need to create an OWIN startup class to initialize SignalR.

Right-click on your project and select "Add > OWIN Startup class" and give it the name "Startup". Click OK.

Replace the Configuration function with the following code:

C#
public void Configuration(IAppBuilder app)
{
    app.MapSignalR();
}

Step 04 - Create a hub

Lets create a Hubs-folder and add a hub to send information to our client in real time.

Right-click on the project and select "Add > New Folder" and give it the name "Hubs".

Lets add the hub by right-clicking on the "Hubs"-folder and select "Add > New Item > SignalR Hub Class (v2)" and name it "MarketDataHub".

We still need to add the reference to our API from Part 1 so that we can use it within our application. Compile the project from Part 1 to create the DLL. Add a reference to it by right-clicking on the "References" node under the web project and selecting "Add Reference > Browse > Select the DLL from Part 1 > OK". You should now be able to reference the project within your web application

Lets jump back to the MarketDataHub.

Add the following field to the hub:

C#
private readonly Websocket socket = null;

And initialize it within the hub's constructor:

C#
public MarketDataHub()
{
    socket = new Websocket(new API.Models.ApiCredentials
    {
        ApiKey = "[YOUR-API-KEY]",
        ApiSecret = "[YOUR-API-SECRET]"
    });
}

Lets build our project to see that everything is working. Press Ctrl+Shift+B.

Note: If you receive the following error: "Multiple assemblies with equivalent identity have been imported...", right-click on the System.Xml.ReaderWriter under "References" in the web project and remove it. Rebuild to ensure everything is working.

Step 05 - Subscribe to events

Now that our websocket is initialized, lets subscribe to the OnClientReady and OnTradeMessage events.

Add the following code in the hub's constructor after initializing the socket:

C#
public MarketDataHub()
{
    // Previous code to initialize socket...

    this.socket.OnClientReady += Socket_OnClientReady;
    this.socket.OnTradeMessage += Socket_OnTradeMessage;
}

And create the functions to handle these events appropriately:

C#
private void Socket_OnClientReady()
{
    // Subscribe to a trade channel
}

private void Socket_OnTradeMessage(string exchange, string primaryCurrency, string secondaryCurrency, API.Models.TradeItem trade)
{
    // Code to push information to the user
}

The purpose of these functions, as described in Part 1, is to be notified when the client is ready to receive commands (ready to subscribe to channels) and to be notified when a new trade message arrives.

Step 06 - Subscribe to Trade Channel

To subscribe to a trade channel, we can add the following code to our Socket_OnClientReady function:

C#
this.socket.SubscribeToTradeChannel("BMEX", "XBT", "USD");

We will now start receiving information from this trade channel within the Socket_OnTradeMessage function.

Step 07 - Connect and push trade messages to client

We only want to send an update to the user once a second. We will need to add a lock to our function to allow only one thread at a time to push messages. We also need to add a field to inidicate whether we should wait before sending another update. Lets add those two fields to our MarketDataHub class:

C#
private readonly object objLock = new object();

private bool doWait = false;

And update our Socket_OnTradeMessage function:

C#
lock (this.objLock)
{
    if (this.doWait)
    {
        Thread.Sleep(1000); // Only send updates once a second
        this.doWait = false;
    }

    Clients.All.broadcastTradePrice(exchange, primaryCurrency, trade.Price);
    this.doWait = true;
}

When a user now subscribes to the broadcastTradePrice function on the client side, they will start to receive updates.

Finally, we need to connect to our socket after wiring up all the events within the hub's constructor. Add the following line at the end of the constructor:

C#
// Initialize socket

// Wire up events

this.socket.Connect();

Now our hub is complete. Lets move onto the client side.

Step 08 - Create an HTML page

Right-click on your project and select "Add > HTML Page" and call it "index.html". Replace its contents with the following code:

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Websocket Example</title>

    <!-- Styles -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/epoch/0.8.4/css/epoch.min.css" />
</head>
<body>

    <h1> Gauge: </h1>
    <div id="gaugeChart" class="epoch gauge-small"></div>

    <h1> Area chart: </h1>
    <div id="areaChart" class="epoch" style="width: 100%; height: 300px;border:solid 1px #C0C0C0;"></div>

    <h1> Line chart: </h1>
    <div id="lineChart" class="epoch" style="width: 100%; height: 300px;border:solid 1px #C0C0C0;"></div>

    <!-- Scripts -->
    <script src="Scripts/jquery-1.6.4.min.js"></script>

    <script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
    <script src="signalr/hubs"></script>

    <script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/epoch/0.8.4/js/epoch.min.js"></script>

    <script src="Scripts/scripts.js"></script>
</body>
</html>

This page will show a line, area and gauge chart.

Step 09 - Create a JavaScript file

Lets add some JavaScript to subscribe to the events coming from the server.

Right-click on the "Scripts"-folder and select "Add > JavaScript File" and call it "scripts.js".

We can start by initializing our charts using the following code within our scripts file:

JavaScript
$(function () {

    // Declare area chart
    var areaChart = $('#areaChart').epoch({
        type: 'time.area',
        data: [{ values: [] }],
        axes: ['left', 'bottom', 'right']
    });

    // Declare line chart
    var lineChart = $('#lineChart').epoch({
        type: 'time.line',
        data: [{ values: [] }],
        axes: ['left', 'bottom', 'right']
    });

    // Declare gauge chart
    var gaugeChart = $('#gaugeChart').epoch({
        type: 'time.gauge',
        value: previousPrice
    });

    // Add some code here to subscribe to the server's events
});

Now we can subscribe to the SignalR hub we created. Add the following 3 fields to the script after initializing the charts:

JavaScript
// Declare a proxy to reference the hub.
var marketDataHub = $.connection.marketDataHub;

// Get a time stamp
var timestamp = ((new Date()).getTime() / 1000) | 0;

var previousPrice = 0;

The timestamp field will be used to track time on the x-axis of our charts. The y-axis will show the trade market prices.

The previousPrice field is used by our gauge chart. We use this value to determine the average price increase/decrease of the market data. (The gauge chart shows its value in percentages.)

Step 10 - Subscribe and update charts

Everything is setup and we can start listening to messages being sent from the server. Lets add the following function to our script file:

JavaScript
// This invoked from the server side in the MarketDataHub.cs file
marketDataHub.client.broadcastTradePrice = function (exchange, primaryCurrency, price) {

    // Update the area chart
    areaChart.push([{ time: timestamp, y: price }]);

    // Update the line chart
    lineChart.push([{ time: timestamp, y: price }]);

    // If the previous price has not been initialized, make the previous price the current price.
    previousPrice = previousPrice == 0 ? price : previousPrice;

    // Calculate performance
    var performance = price / previousPrice;

    // Update the gauge chart
    gaugeChart.update(performance);

    // Increase time stamp
    timestamp++;

    console.log(price);

    previousPrice = price;
};

The parameters for the line and area charts are timestamp and y. Time is displayed on the x-axis and prices are displayed on the y-axis.

Next we run a calculation to determine what the percentage increase/decrease is to update our gauge chart. After all charts have been updated, we increase our timestamp and wait for the next message to be sent from the server. Finally we can start the connection to the hub. Add the following code after the above function:

JavaScript
// Start the connection to the market hub
$.connection.hub.start();

Press F5 to debug your web application. You should see your charts being updated in real time:

Final Words

We are now able to use our API within our web application to show the user real time trade market data in a visual, graphical format. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)