A Step-by-Step Tutorial of Building a Simple Peer-to-Peer WebSocket App – Part 5

In Part 1 we looked at the completed application, then reviewed the starting code in Part 2. In Part 3 we created a WebSocket connection, then started sending and receiving messages in Part 4.

At the end of Part 4, we noticed an issue where we “heard the echo” of (receiving) the same messages we just sent to all the other clients. In this part, we’ll solve this problem.

Task 9: Creating message property and message type variables

There are certain message properties and message types that are used throughout the example. To make our code more readable, we’re introducing variables for them. We’ll create the following message properties:

[sourcecode language=”javascript” light=”true” gutter=”0″]
var MESSAGE_PROPERTIES = {
‘messageType’: ‘MESSAGE_TYPE’,
‘userId’: ‘USERID’,
‘sliderPos’: ‘SLIDER_POS’
};
[/sourcecode]

In our example we use only one message type, but in more involved cases you may have several of them:

[sourcecode language=”javascript” light=”true” gutter=”0″]
var MESSAGE_TYPES = {
"sliderMoved": "SLIDER_MOVED"
};
[/sourcecode]

Here’s the entire JavaScript file. Look for Task 9 in the comments to find the newly added function.

[sourcecode language=”javascript” collapse=”1″ highlight=”8,9,10,11,12,13,14,15,16,17,18,19″ wraplines=”false”]
// Variables you can change
//
var WEBSOCKET_URL = "wss://demos.kaazing.com/jms";
var TOPIC_NAME = "/topic/myTopic";
var IN_DEBUG_MODE = true;
var DEBUG_TO_SCREEN = true;

/*** Task 9 ***/
// Message Properties and Message Types
var MESSAGE_PROPERTIES = {
"messageType": "MESSAGE_TYPE",
"userId": "USERID",
"sliderPos": "SLIDER_POS"
};

var MESSAGE_TYPES = {
"sliderMoved": "SLIDER_MOVED"
};
/*** Task 9 ***/

// WebSocket and JMS variables
//
var connection;
var session;
var wsUrl;

// Variable for log messages
//
var screenMsg = "";

// JSFiddle-specific variables
//
var runningOnJSFiddle = (window.location.hostname === "fiddle.jshell.net");

// Used for development and debugging. All logging can be turned
// off by modifying this function.
//
var consoleLog = function(text) {
if (IN_DEBUG_MODE) {
if (runningOnJSFiddle || DEBUG_TO_SCREEN) {
// Logging to the screen
 screenMsg = screenMsg + text + "<br>";
$("#logMsgs").html(screenMsg);
} else {
// Logging to the browser console
console.log(text);
}
}
};

var handleException = function(e) {
consoleLog("EXCEPTION: " + e);
};
// *** Task 6a ***
var handleTopicMessage = function(message) {
// *** Task 8b ***
$("#slider").val(message.getText());
// *** Task 8b ***
};
// *** Task 6a ***
// *** Task 2 ***
var sliderChange = function(sliderValue) {
consoleLog("Slider changed: " + sliderValue);
// *** Task 8a ***
doSend(session.createTextMessage(sliderValue));
// *** Task 8a ***
};
// *** Task 2 ***
// *** Task 7a ***
// Send a message to the topic.
//
var doSend = function(message) {
topicProducer.send(null, message, DeliveryMode.NON_PERSISTENT, 3, 1, function() {

});
consoleLog("Message sent: " + message.getText());
};
// *** Task 7a ***
// Connecting…
//
var doConnect = function() {
// Connect to JMS, create a session and start it.
//
var jmsConnectionFactory = new JmsConnectionFactory(WEBSOCKET_URL);
try {
var connectionFuture = jmsConnectionFactory.createConnection(”, ”, function() {
if (!connectionFuture.exception) {
try {
connection = connectionFuture.getValue();
connection.setExceptionListener(handleException);

consoleLog("Connected to " + WEBSOCKET_URL);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// *** Task 3 ***
// Creating topic
var myTopic = session.createTopic(TOPIC_NAME);
consoleLog("Topic created…");
// *** Task 3 ***
// *** Task 4 ***
// Creating topic Producer
topicProducer = session.createProducer(myTopic);
consoleLog("Topic producer created…");
// *** Task 4 ***
// *** Task 5 ***
// Creating topic Consumer
topicConsumer = session.createConsumer(myTopic);
consoleLog("Topic consumer created…");
// *** Task 5 ***
// *** Task 6b ***
topicConsumer.setMessageListener(handleTopicMessage);
// *** Task 6b ***
connection.start(function() {
// Put any callback logic here.
//
consoleLog("JMS session created");
// *** Task 7b ***
doSend(session.createTextMessage("Hello world…"));
// *** Task 7b ***
});
} catch (e) {
handleException(e);
}
} else {
handleException(connectionFuture.exception);
}
});
} catch (e) {
handleException(e);
}
};
[/sourcecode]

To see and run the updated code in JSFiddle, point your browser to the Task 9 state: https://jsfiddle.net/kaazing/02grLddv/

Task 10: Creating a unique userID

The easiest way to filter the messages sent by our client is by attaching a unique user, or client ID, to each message. Upon receiving a new message, the client will only process it if the message was sent by one of our peers. If it was sent by our own client, we ignore it.

A simple way to obtain such a (pseudo)unique ID, by generating a large enough random number.

Note: Using a random number to uniquely identify our clients is of course a poor man’s technique. In a production environment, you should use a technique that guarantees the uniqueness of your client IDs.

[sourcecode language=”javascript” light=”true” gutter=”0″]
var userId = Math.random(100000).toString();
[/sourcecode]

Here’s the entire JavaScript file. Look for Task 10 in the comments to find the newly added function.

[sourcecode language=”javascript” collapse=”1″ highlight=”21,22,23″ wraplines=”false”]
// Variables you can change
//
var WEBSOCKET_URL = "wss://demos.kaazing.com/jms";
var TOPIC_NAME = "/topic/myTopic";
var IN_DEBUG_MODE = true;
var DEBUG_TO_SCREEN = true;

/*** Task 9 ***/
// Message Properties and Message Types
var MESSAGE_PROPERTIES = {
"messageType": "MESSAGE_TYPE",
"userId": "USERID",
"sliderPos": "SLIDER_POS"
};

var MESSAGE_TYPES = {
"sliderMoved": "SLIDER_MOVED"
};
/*** Task 9 ***/

/*** Task 10 ***/
var userId = Math.random(100000).toString();
/*** Task 10 ***/

// WebSocket and JMS variables
//
var connection;
var session;
var wsUrl;

// Variable for log messages
//
var screenMsg = "";

// JSFiddle-specific variables
//
var runningOnJSFiddle = (window.location.hostname === "fiddle.jshell.net");

// Used for development and debugging. All logging can be turned
// off by modifying this function.
//
var consoleLog = function(text) {
if (IN_DEBUG_MODE) {
if (runningOnJSFiddle || DEBUG_TO_SCREEN) {
// Logging to the screen
 screenMsg = screenMsg + text + "<br>";
$("#logMsgs").html(screenMsg);
} else {
// Logging to the browser console
console.log(text);
}
}
};

var handleException = function(e) {
consoleLog("EXCEPTION: " + e);
};
// *** Task 6a ***
var handleTopicMessage = function(message) {
// *** Task 8b ***
$("#slider").val(message.getText());
// *** Task 8b ***
};
// *** Task 6a ***
// *** Task 2 ***
var sliderChange = function(sliderValue) {
consoleLog("Slider changed: " + sliderValue);
// *** Task 8a ***
doSend(session.createTextMessage(sliderValue));
// *** Task 8a ***
};
// *** Task 2 ***
// *** Task 7a ***
// Send a message to the topic.
//
var doSend = function(message) {
topicProducer.send(null, message, DeliveryMode.NON_PERSISTENT, 3, 1, function() {

});
consoleLog("Message sent: " + message.getText());
};
// *** Task 7a ***
// Connecting…
//
var doConnect = function() {
// Connect to JMS, create a session and start it.
//
var jmsConnectionFactory = new JmsConnectionFactory(WEBSOCKET_URL);
try {
var connectionFuture = jmsConnectionFactory.createConnection(”, ”, function() {
if (!connectionFuture.exception) {
try {
connection = connectionFuture.getValue();
connection.setExceptionListener(handleException);

consoleLog("Connected to " + WEBSOCKET_URL);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// *** Task 3 ***
// Creating topic
var myTopic = session.createTopic(TOPIC_NAME);
consoleLog("Topic created…");
// *** Task 3 ***
// *** Task 4 ***
// Creating topic Producer
topicProducer = session.createProducer(myTopic);
consoleLog("Topic producer created…");
// *** Task 4 ***
// *** Task 5 ***
// Creating topic Consumer
topicConsumer = session.createConsumer(myTopic);
consoleLog("Topic consumer created…");
// *** Task 5 ***
// *** Task 6b ***
topicConsumer.setMessageListener(handleTopicMessage);
// *** Task 6b ***
connection.start(function() {
// Put any callback logic here.
//
consoleLog("JMS session created");
// *** Task 7b ***
doSend(session.createTextMessage("Hello world…"));
// *** Task 7b ***
});
} catch (e) {
handleException(e);
}
} else {
handleException(connectionFuture.exception);
}
});
} catch (e) {
handleException(e);
}
};
[/sourcecode]

To see and run the updated code in JSFiddle, point your browser to the Task 10 state: https://jsfiddle.net/kaazing/q762tv9y/.

Task 11: Attaching the client ID to the messages

Now that we have a unique ID, let’s attach it to every message we send out, in the form of a message property.

[sourcecode language=”javascript” light=”true”]
message.setStringProperty(MESSAGE_PROPERTIES.userId, userId);
[/sourcecode]

Here’s the entire JavaScript file. Look for Task 11 in the comments to find the newly added function.

[sourcecode language=”javascript” collapse=”1″ highlight=”77,78,79″ wraplines=”false”]
// Variables you can change
//
var WEBSOCKET_URL = "wss://demos.kaazing.com/jms";
var TOPIC_NAME = "/topic/myTopic";
var IN_DEBUG_MODE = true;
var DEBUG_TO_SCREEN = true;

/*** Task 9 ***/
// Message Properties and Message Types
var MESSAGE_PROPERTIES = {
"messageType": "MESSAGE_TYPE",
"userId": "USERID",
"sliderPos": "SLIDER_POS"
};

var MESSAGE_TYPES = {
"sliderMoved": "SLIDER_MOVED"
};
/*** Task 9 ***/

/*** Task 10 ***/
var userId = Math.random(100000).toString();
/*** Task 10 ***/

// WebSocket and JMS variables
//
var connection;
var session;
var wsUrl;

// Variable for log messages
//
var screenMsg = "";

// JSFiddle-specific variables
//
var runningOnJSFiddle = (window.location.hostname === "fiddle.jshell.net");

// Used for development and debugging. All logging can be turned
// off by modifying this function.
//
var consoleLog = function(text) {
if (IN_DEBUG_MODE) {
if (runningOnJSFiddle || DEBUG_TO_SCREEN) {
// Logging to the screen
 screenMsg = screenMsg + text + "<br>";
$("#logMsgs").html(screenMsg);
} else {
// Logging to the browser console
console.log(text);
}
}
};

var handleException = function(e) {
consoleLog("EXCEPTION: " + e);
};
// *** Task 6a ***
var handleTopicMessage = function(message) {
// *** Task 8b ***
$("#slider").val(message.getText());
// *** Task 8b ***
};
// *** Task 6a ***
// *** Task 2 ***
var sliderChange = function(sliderValue) {
consoleLog("Slider changed: " + sliderValue);
// *** Task 8a ***
doSend(session.createTextMessage(sliderValue));
// *** Task 8a ***
};
// *** Task 2 ***
// *** Task 7a ***
// Send a message to the topic.
//
var doSend = function(message) {
/*** Task 11 ***/
message.setStringProperty(MESSAGE_PROPERTIES.userId, userId);
/*** Task 11 ***/
topicProducer.send(null, message, DeliveryMode.NON_PERSISTENT, 3, 1, function() {

});
consoleLog("Message sent: " + message.getText());
};
// *** Task 7a ***
// Connecting…
//
var doConnect = function() {
// Connect to JMS, create a session and start it.
//
var jmsConnectionFactory = new JmsConnectionFactory(WEBSOCKET_URL);
try {
var connectionFuture = jmsConnectionFactory.createConnection(”, ”, function() {
if (!connectionFuture.exception) {
try {
connection = connectionFuture.getValue();
connection.setExceptionListener(handleException);

consoleLog("Connected to " + WEBSOCKET_URL);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// *** Task 3 ***
// Creating topic
var myTopic = session.createTopic(TOPIC_NAME);
consoleLog("Topic created…");
// *** Task 3 ***
// *** Task 4 ***
// Creating topic Producer
topicProducer = session.createProducer(myTopic);
consoleLog("Topic producer created…");
// *** Task 4 ***
// *** Task 5 ***
// Creating topic Consumer
topicConsumer = session.createConsumer(myTopic);
consoleLog("Topic consumer created…");
// *** Task 5 ***
// *** Task 6b ***
topicConsumer.setMessageListener(handleTopicMessage);
// *** Task 6b ***
connection.start(function() {
// Put any callback logic here.
//
consoleLog("JMS session created");
// *** Task 7b ***
doSend(session.createTextMessage("Hello world…"));
// *** Task 7b ***
});
} catch (e) {
handleException(e);
}
} else {
handleException(connectionFuture.exception);
}
});
} catch (e) {
handleException(e);
}
};
[/sourcecode]

To see and run the updated code in JSFiddle, point your browser to Task 11 state: https://jsfiddle.net/kaazing/6ro54rtz/.

Task 12: Ignoring messages sent by the same client

The last step is to throw away the messages that were sent by us. To do so, we’ll inspect the userId message property of every message. If it’s the same as our own userId, we ignore it; otherwise, we process it.

[sourcecode language=”javascript” light=”true”]
var handleTopicMessage = function(message) {
// *** Task 12 ***
if (message.getStringProperty(MESSAGE_PROPERTIES.userId) != userId) {
consoleLog(‘Message received: ‘ + message.getText());
// *** Task 8b ***
$(‘#slider’).val(message.getText());
// *** Task 8b ***
}
// *** Task 12 ***
};
[/sourcecode]

Here’s the entire JavaScript file. Look for Task 12 in the comments to find the newly added function.

[sourcecode language=”javascript” collapse=”1″ highlight=”61,62,63,64,65,66,67,68″ wraplines=”false”]
// Variables you can change
//
var WEBSOCKET_URL = "wss://demos.kaazing.com/jms";
var TOPIC_NAME = "/topic/myTopic";
var IN_DEBUG_MODE = true;
var DEBUG_TO_SCREEN = true;

/*** Task 9 ***/
// Message Properties and Message Types
var MESSAGE_PROPERTIES = {
"messageType": "MESSAGE_TYPE",
"userId": "USERID",
"sliderPos": "SLIDER_POS"
};

var MESSAGE_TYPES = {
"sliderMoved": "SLIDER_MOVED"
};
/*** Task 9 ***/

/*** Task 10 ***/
var userId = Math.random(100000).toString();
/*** Task 10 ***/

// WebSocket and JMS variables
//
var connection;
var session;
var wsUrl;

// Variable for log messages
//
var screenMsg = "";

// JSFiddle-specific variables
//
var runningOnJSFiddle = (window.location.hostname === "fiddle.jshell.net");

// Used for development and debugging. All logging can be turned
// off by modifying this function.
//
var consoleLog = function(text) {
if (IN_DEBUG_MODE) {
if (runningOnJSFiddle || DEBUG_TO_SCREEN) {
// Logging to the screen
screenMsg = screenMsg + text + "<br>";
$("#logMsgs").html(screenMsg);
} else {
// Logging to the browser console
console.log(text);
}
}
};

var handleException = function(e) {
consoleLog("EXCEPTION: " + e);
};

// *** Task 6a ***
var handleTopicMessage = function(message) {
// *** Task 12 ***
if (message.getStringProperty(MESSAGE_PROPERTIES.userId) != userId) {
consoleLog("Message received: " + message.getText());
// *** Task 8b ***
$("#slider").val(message.getText());
// *** Task 8b ***
}
// *** Task 12 ***
};
// *** Task 6a ***
// *** Task 2 ***
var sliderChange = function(sliderValue) {
consoleLog("Slider changed: " + sliderValue);
// *** Task 8a ***
doSend(session.createTextMessage(sliderValue));
// *** Task 8a ***
};
// *** Task 2 ***
// *** Task 7a ***
// Send a message to the topic.
//
var doSend = function(message) {
/*** Task 11 ***/
message.setStringProperty(MESSAGE_PROPERTIES.userId, userId);
/*** Task 11 ***/
topicProducer.send(null, message, DeliveryMode.NON_PERSISTENT, 3, 1, function() {

});
consoleLog("Message sent: " + message.getText());
};
// *** Task 7a ***
// Connecting…
//
var doConnect = function() {
// Connect to JMS, create a session and start it.
//
var jmsConnectionFactory = new JmsConnectionFactory(WEBSOCKET_URL);
try {
var connectionFuture = jmsConnectionFactory.createConnection(”, ”, function() {
if (!connectionFuture.exception) {
try {
connection = connectionFuture.getValue();
connection.setExceptionListener(handleException);

consoleLog("Connected to " + WEBSOCKET_URL);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

// *** Task 3 ***
// Creating topic
var myTopic = session.createTopic(TOPIC_NAME);
consoleLog("Topic created…");
// *** Task 3 ***
// *** Task 4 ***
// Creating topic Producer
topicProducer = session.createProducer(myTopic);
consoleLog("Topic producer created…");
// *** Task 4 ***
// *** Task 5 ***
// Creating topic Consumer
topicConsumer = session.createConsumer(myTopic);
consoleLog("Topic consumer created…");
// *** Task 5 ***
// *** Task 6b ***
topicConsumer.setMessageListener(handleTopicMessage);
// *** Task 6b ***
connection.start(function() {
// Put any callback logic here.
//
consoleLog("JMS session created");
// *** Task 7b ***
doSend(session.createTextMessage("Hello world…"));
// *** Task 7b ***
});
} catch (e) {
handleException(e);
}
} else {
handleException(connectionFuture.exception);
}
});
} catch (e) {
handleException(e);
}
};
[/sourcecode]

Run your application again. This time you don’t “hear back” the messages that were sent by you, only the ones sent by others.

To see and run the updated code in JSFiddle, point your browser to the Task 12 state: https://jsfiddle.net/kaazing/xxbkLey9/.

Now that we’ve figured out a solution to our issue, let’s enhance our application. In Part 6 will show you how to add an image to your application and control its size with the slider.