IBM Cloud Satellite: Build faster. Securely. Anywhere. Read more

Set up WebSocket communication using Node-RED between a Jupyter Notebook on IBM Watson Studio and a web interface

In this tutorial, learn how to deploy a WebSocket server using a Node-RED flow. After the WebSocket server is running, you’ll see how to send messages to the server from a web client and a Jupyter Notebook.

WebSocket is a communications protocol that expands a typical TCP connection to establish a long-running persistent connection. This is beneficial because it allows for two-way communication between a client and server, which greatly improves real-time performance and decreases overhead. It is very flexible and can be used for transferring data to and from servers using a browser, Peer-to-Peer (P2P), or direct communication between browsers. WebSockets are used whenever you need a truly low latency, near real-time connection between the client and the server. Unlike HTTP, the socket that is connected to the server stays open for communication. That means data can be pushed to the browser in real time on demand.

In most cases, if you’d like to use WebSocket, you must write code and use various libraries such as Socket.IO, Phpws, and Ws4py. In this tutorial, we show how to alternatively use flow-based programming in Node-RED to set up a WebSocket server. Node-RED is an open source tool that provides an alternative to traditional programming in which users can configure various nodes (HTTP, MQTT, and JSON) and wire the nodes together.

Learning objectives

In this tutorial, you learn how to:

  • Set up a WebSocket server using Node-RED
  • Communicate with a WebSocket server through two client types: a web interface and a Jupyter Notebook

Prerequisites

To complete this tutorial, you need:

Estimated time

It should take you approximately 30 minutes to complete the tutorial.

Steps

Provision Node-RED instance

  1. Navigate to the IBM Cloud catalog.
  2. Search for node red, and select Node-RED App. Selecting Node-RED app

  3. Scroll down and click Create. This provisions a lite instance of a Cloudant database as well, which is used to store Node-RED flows. Creating Cloudant database

Client/server communication between a Jupyter Notebook on IBM Watson Studio and a web interface

This section explains how to set up the client and server communication between a Jupyter Notebook and a web interface.

Client/server communication

Set up WebSocket server on Node-RED

  1. Navigate to the Node-RED URL by searching for WebSocket in the Filter Nodes search bar.

    There are two WebSocket nodes that can be selected, websocket in and websocket out. Both the input and output WebSocket nodes can be configured as either a server or client. In server mode, they “listen on” a URL, and in client mode they connect to a specified IP address.

    The websocket in node makes an endpoint available for a client to connect and send messages to. The websocket out node makes an additional endpoint available to send outbound messages to connected clients.

    In this tutorial, you create your WebSocket server using the following nodes:

    • websocket in
    • websocket out
    • function

      Flows that create a web socket

  2. Drag a WebSocket input node to the flow.

  3. Double-click the node to configure it.
  4. Set the type as Listen on.
  5. In the Path section, you define an endpoint that clients can connect to by adding a new websocket-listener with the path /ws/receiveMessage. After adding this node, the flow opens a WebSocket service at the specified path.

    Editing WebSocket in node

  6. Select a WebSocket output node.

  7. Set the type as Listen on.
  8. Add a new websocket-listener with the path /ws/publishMessage. Clients that listen on this endpoint receive all messages that are broadcast from the WebSocket server.

    Setting WebSocket output node

  9. Search for function in the Filter nodes search bar. Drag a function node between the WebSocket nodes.

    Searching for function

  10. Here, you use the function node to redirect incoming messages received at the /ws/receiveMessage endpoint, and broadcast the received message payload through the /ws/publishMessage endpoint. Double-click the function node, and add the following code in the function node.

     return {payload: msg.payload};
    

    Redirecting messages

    When you have two clients (for example, two browsers) sending and receiving data on the same Node-RED WebSocket, the message is sent back only to the client that triggered the flow. With the current configuration, messages received by the /ws/receiveMessage endpoint are returned only to the client that originally sent the message. You’ll adjust your function to broadcast the message to all connected clients. To do this, adjust your function to remove the msg._session property from incoming messages. Adjust the function node to contain the following code.

     ```
     msg._session = "";
     return msg;
     ```
    
  11. Deploy the flow by clicking Deploy on the top of the page.

Create a WebSocket client within a web application

Now that you have your WebSocket server properly configured, you’ll create a simple test web page to act as a client, and publish messages to the WebSocket server. To do this, you’ll use an HTTP in, Template, and an HTTP out node.

  1. Create a new flow, search for HTTP, and drag an HTTP in and HTTP out node to your flow. Drag a Template node between the HTTP nodes. The HTTP in node allows for users to access an endpoint in their browser. The Template node specifies the HTML that is returned to the user.

  2. Select the HTTP in node, and enter /access-url in the URL field.

    Selecting the HTTP in node

  3. Now, configure your HTML template. Copy and paste the following code into the template. Also, enter the URL to your Node-RED instance in the first line. I explain what is happening in each section below the code block.

     <script>
     var url = "<node-red-url>"
     var incomingUrl = `ws://${url}/ws/receiveMessage`
     var outgoingUrl = `ws://${url}/ws/publishMessage`
     console.log(outgoingUrl)
     var socket1 = new WebSocket(incomingUrl);
     var socket2 = new WebSocket(outgoingUrl);
    
     socket2.onopen = function() {
       console.log("client connected")
       var message = {
         'cmd': 'Client connected'
       };
       socket1.send(JSON.stringify(message));
     };
    
     socket2.onclose = function(){
       console.log('Connection closed');
     };
    
     socket2.onerror = function(error) {
       console.log('Error detected: ' + error);
     };
    
     socket2.onmessage = function(e) {
       console.log(server_message)
       var server_message = e.data;
       document.getElementById('message_receive').innerHTML += `<p>${server_message}</p>`
       alert(server_message)
     }
    
     var sendMessage = function() {
       let message = document.getElementById('message_send')
       let messageRaw = message.textContent
       console.log(`sending message ${messageRaw}`)
       socket1.send(messageRaw);
     }
    
     </script>
     <body>
       <h3>Send Message</h3>
       <input type="text" id="message_send" >
       <button type="button" ng-click="">Send</button>
       <h3>Received Message</h3>
       <div id="message_receive"></div>
     </body>
    

    Editing template node

    At the beginning of the page, you’ll create a script, and begin by establishing connections to the WebSocket endpoints.

     var socket1 = new WebSocket("ws://${url}/ws/receiveMessage");
     var socket2 = new WebSocket("ws://${url}/ws/publishMessage");
    

    This creates two new WebSocket objects pointing to the two receive and publish endpoints. Note that these URLs are prefixed with ws instead of http.

    After defining the connections, you then define event handlers for the WebSocket objects. This runs specific functions when the WebSocket connection is opened, closed, and so on. For example, you’ll configure the onopen event to send a message to the /ws/receiveMessage channel after the browser establishes a connection to the /ws/publishMessage WebSocket channel.

     socket2.onopen = function() {
       var message = {
         'cmd': 'Client connected'
       };
       socket1.send(JSON.stringify(message));
     };
    

    Whenever the client receives a message from the server on the /ws/publishMessage channel, the onMessage() event is triggered. Here, you’ll simply print the message in an alert window.

     socket2.onmessage = function(e) {
       console.log(server_message)
       var server_message = e.data;
       document.getElementById('message_receive').innerHTML += `<p>${server_message}</p>`
       alert(server_message)
     }
    

    Next, add a <body> tag to render incoming messages in the webpage. Within the body tag, you’ll define a paragraph tag <p> to render messages received from the WebSocket server. You’ll also create a text input box.

     <body>
       <h3>Send Message</h3>
       <input type="text" id="message_send" >
       <button type="button" ng-click="sendMessage()">Send</button>
       <h3>Received Message</h3>
       <div id="message_receive"></div>
     </body>
    
  4. Finally, add an HTTP out node. Connect all the nodes together, and then click Deploy.

    Adding HTTP out node

After you complete the setup for the clients and server, the flow is ready to test. The server broadcasts any received messages to all connected clients. Messages should be handled properly and programmatically at both clients for appropriate action.

In your browser, open a webpage at /access-url. You should then see a simple HTML page with a button and input box. A pop-up alert window should also show that the client successfully connected.

Send message

If you check the Node-RED again, you’ll see that the WebSocket nodes have been connected.

WebSocket node connected

Enter a message in the webpage input box, and click Send. The message payload should then show up in the debug section of the Node-RED flow.

Message payload

Set up WebSocket client on Jupyter Notebook

In the previous section, you learned how to create a WebSocket client in a web browser and define event handlers to handle incoming messages. In this section, you learn how to do the same from a Jupyter Notebook. First, you’ll set up your Jupyter environment.

  1. Navigate to the IBM Cloud console, and search for Watson Studio.

    Selecting Watson Studio

  2. Select the pricing plan, and click Create. If deploying on an IBM Lite account, be sure to select the free Lite plan

  3. Click Create a project.

    Creating a project

  4. Select Create an empty project, and click Create.

    Selecting Empty project

    After the project is created, a dashboard showing all of the project assets is displayed. You’ll continue by adding a Jupyter Notebook to your project.

  5. Select Add to project in the upper right of the page, then click New notebook.

    New notebook

  6. After your notebook loads, install the WebSocket Python client in the first cell.

     ! pip install websocket-client
    
  7. Paste the following code into the next cell.

     from websocket import create_connection
     url = "<node-red-url>"
     channel = "receviveMessage"
     ws_endpoint = f"ws://{url}/ws/{channel}"
     print(f"connecting to {ws_endpoint}")
     ws = create_connection(ws_endpoint)
     ws.send("Sending message from Notebook")
     ws.close()
    

    The ws.send method is used in this example to forward messages to the WebSocket server.

  8. Click Run on the Jupyter Notebook, and you should see a message in the Node-RED debug section

Summary

You have set up a WebSocket server on Node-RED and WebSocket clients on a web interface and on a Jupyter Notebook on IBM Watson Studio. Deploy your flow and use it for your application.