How to use html5 websockets within sharedworkers - javascript

After checking similar questions on stackoverflow I did not find anything much helpful for what I want to do in my project. Reading and researching I successfully made the application work having multiple connections to my Ratchet PHP websocket server, but I noticed every time the user reloaded a page or opened a link in a new tab, the client websocket got disconnected and then reconnected again.
So, I wonder how to get only one persistent connection to a WebSocket Server, for multiple users, in a web application using a Sharedworker.
What I have in the client side is this:
<script>
$(document).ready(function($) {
let socket = new WebSocket("ws://realtime:8090");
socket.onopen = function(e) {
console.log("Browser client connected to websocket server");
socket.send("Greetings from the browser!");
};
socket.onmessage = function(event) {
console.log('Data received from server: ' + event.data);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`Connection closed cleanly, code=${event.code} reason=${event.reason}`);
}
else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('Connection closed unexpectedly.');
}
};
socket.onerror = function(error) {
alert(error.message);
};
});
</script>

Ok after reading, researching and trying different things and code samples, I came to this solution:
The client side (browser) should have the connection to a Sharedworker.
The sharedworker is a separated javascript file containing the core of the sharedworker and whatever other JS code that needs to be executed within it.
I first tested the sharedworker to work fine with the browser tabs, counting the number of opened tabs per user and sharing messages to one user, and then to a group of users.
Once the communication between the browser and the Sharedworker passed those tests I added the websocket code to the body of the Sharedworker JS file.
In the end, the client side (browser) looks like this:
<script>
$(document).ready(function($) {
var currentUser = "{{ Auth::user()->name }}";
let worker = new SharedWorker('worker.js');
worker.port.start();
worker.port.postMessage({
action: 'connect',
username: currentUser
});
worker.port.onmessage = function(message) {
console.log(message.data);
};
});
</script>
The Sharedworker looks like this:
// All this code is executed only once, until the onconnect() function.
//---------------------------------------------------------------------
// The array AllPorts contains objects with the format {user:<string>, port:<MessagePort>}
let AllPorts = [];
var socket = new WebSocket("ws://ssa:8090");
// Called when the WebSocket Server accepts the connection.
socket.onopen = function(e) {
//
};
// Event handler fired when the WebSocket Server sends a message to this client.
socket.onmessage = function(e) {
var message = JSON.parse(e.data);
// This loop sends a message to each tab opened by the given user.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == message.to) {
AllPorts[i].port.postMessage(message.msg);
}
}
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log('Connection closed normally');
}
else {
console.log('Connection closed unexpectedly.');
}
};
socket.onerror = function(error) {
console.log(error.message);
};
// This event handler is fired every time a new tab is opened on the web browser.
onconnect = function(ev) {
let port = ev.ports[0];
port.onmessage = function(e) {
console.log(e.data.action);
let currentUser = e.data.username;
let userIsConnected = false;
switch (e.data.action) {
case "connect":
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
// Add new connected tab to AllPorts array.
AllPorts.push({user: currentUser, port: port});
if (!userIsConnected) {
// New users are added to the list of the WebSocket Server.
setTimeout(() => {
socket.send(JSON.stringify({action: 'connect', username: currentUser}));
}, 600);
}
break;
case "close":
console.log(AllPorts);
var index;
// This is also executed when the user reloads the Tab.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].port == port) {
index = i;
currentUser = AllPorts[i].user;
}
}
AllPorts.splice(index, 1);
userIsConnected = false;
// Check for any connected tab.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
if (!userIsConnected) {
// User doen't have more tabs opened. Remove user from WebSocket Server.
socket.send(JSON.stringify({action: 'disconnect', username: currentUser}));
}
break;
case "notify":
// Check if given user is connected.
for (var i = 0; i < AllPorts.length; i++) {
if (AllPorts[i].user == currentUser) {
userIsConnected = true;
}
}
if (userIsConnected) {
socket.send(JSON.stringify({action: 'notify', to: currentUser, message: e.data.message}));
}
} // switch
} // port.onmessage
} // onconnect

Related

how to send commands to AWS Session manager websocket url using xterm.js?

I have a websocket url created by AWS. URL is created by aws ssm start session using .net sdk.
Start session method gives me streamUrl, token and session ID.
URL is in following format:
wss://ssmmessages.ap-south-1.amazonaws.com/v1/data-channel/sessionidhere?role=publish_subscribe
There is actual session id at placeof "sessionidhere" that I can not share.
I want to open terminal on web using xterm.js. I've read that xterm.js can connect to websocket URL, send messages and receive outputs.
My javascript code is here :
<!doctype html>
<html>
<head>
<link href="~/xterm.css" rel="stylesheet" />
<script src="~/Scripts/jquery-3.4.1.js"></script>
<script src="~/Scripts/bootstrap.js"></script>
<script src="~/xterm.js"></script>
</head>
<body>
<div id="terminal"></div>
<script type="text/javascript">
var term = new Terminal({
cursorBlink: "block"
});
var curr_line = "";
var entries = [];
term.open(document.getElementById('terminal'));
const ws = new WebSocket("wss://ssmmessages.ap-south-1.amazonaws.com/v1/data-channel/sessionid?role=publish_subscribe?token=tokenvalue");
var curr_line = "";
var entries = [];
term.write("web shell $ ");
term.prompt = () => {
if (curr_line) {
let data = {
method: "command", command: curr_line
}
ws.send(JSON.stringify(data));
}
};
term.prompt();
ws.onopen = function (e) {
alert("[open] Connection established");
alert("Sending to server");
var enc = new TextEncoder("utf-8"); // always utf-8
// console.log(enc.encode("This is a string converted to a Uint8Array"));
var data = "ls";
console.log(enc.encode(data));
alert(enc.encode(data));
ws.send(enc.encode(data));
alert(JSON.stringify(e));
};
ws.onclose = function (event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
alert('[close] Connection died');
}
};
ws.onerror = function (error) {
alert(`[error] ${error.message}`);
};
// Receive data from socket
ws.onmessage = msg => {
alert(data);
term.write("\r\n" + JSON.parse(msg.data).data);
curr_line = "";
};
term.on("key", function (key, ev) {
//Enter
if (ev.keyCode === 13) {
if (curr_line) {
entries.push(curr_line);
term.write("\r\n");
term.prompt();
}
} else if (ev.keyCode === 8) {
// Backspace
if (curr_line) {
curr_line = curr_line.slice(0, curr_line.length - 1);
term.write("\b \b");
}
} else {
curr_line += key;
term.write(key);
}
});
// paste value
term.on("paste", function (data) {
curr_line += data;
term.write(data);
});
</script>
</body>
</html>
Now, the session is being opened, I am getting alert of connection established. It's being successful connection, but whenever I try to send commands, the connection is being closed by saying 'request to open data channel does not contain a token'. I've tried to send command in 3 ways.
First is :
ws.send("ls")
second:
let data = {
method: "command", command: curr_line
}
ws.send(JSON.stringify(data));
But facing same error i.e. request to open data channel does not contain token, connection died
third:
var enc = new TextEncoder("utf-8");
var data = "ls";
ws.send(enc.encode(data));
For third, I'm not getting any error, but not getting output too... Can someone please help?
The protocol used by AWS Session manager consists of the following :
open a websocket connection on the stream URL
send an authentication request composed of the following JSON stringified :
{
"MessageSchemaVersion": "1.0",
"RequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"TokenValue": "<YOUR-TOKEN-VALUE>"
}
From this moment the protocol is not JSON anymore. It is implemented in the offical Amazon SSM agent which is required if you want start a SSM session from the AWS CLI. The payload must be sent & receive according this binary format
I had exactly the same requirement as you few months ago so I've made an AWS Session manager client library : https://github.com/bertrandmartel/aws-ssm-session for nodejs and browser. If you want more information about how the protocol works, checkout this
The sample code available for browser use xterm.js
First clone the project and generate websocket URL and token using aws-api with some utility script :
git clone git#github.com:bertrandmartel/aws-ssm-session.git
cd aws-ssm-session
npm i
npm run build
node scripts/generate-session.js
which gives you :
{
SessionId: 'xxxxxx-xxxxxxxxxxxxxx',
TokenValue: 'YOUR_TOKEN',
StreamUrl: 'wss://ssmmessages.eu-west-3.amazonaws.com/v1/data-channel/user-xxxxxxxxxxxxxx?role=publish_subscribe'
}
Then serve the sample app :
npm install http-server -g
http-server -a localhost -p 3000
go to http://localhost:3000/test/web, enter the websocket URI and token :
The sample code for browser :
import { ssm } from "ssm-session";
var socket;
var terminal;
const termOptions = {
rows: 34,
cols: 197
};
function startSession(){
var tokenValue = document.getElementById("tokenValue").value;
var websocketStreamURL = document.getElementById("websocketStreamURL").value;
socket = new WebSocket(websocketStreamURL);
socket.binaryType = "arraybuffer";
initTerminal()
socket.addEventListener('open', function (event) {
ssm.init(socket, {
token: tokenValue,
termOptions: termOptions
});
});
socket.addEventListener('close', function (event) {
console.log("Websocket closed")
});
socket.addEventListener('message', function (event) {
var agentMessage = ssm.decode(event.data);
//console.log(agentMessage);
ssm.sendACK(socket, agentMessage);
if (agentMessage.payloadType === 1){
terminal.write(agentMessage.payload)
} else if (agentMessage.payloadType === 17){
ssm.sendInitMessage(socket, termOptions);
}
});
}
function stopSession(){
if (socket){
socket.close();
}
terminal.dispose()
}
function initTerminal() {
terminal = new window.Terminal(termOptions);
terminal.open(document.getElementById('terminal'));
terminal.onKey(e => {
ssm.sendText(socket, e.key);
});
terminal.on('paste', function(data) {
ssm.sendText(socket, data);
});
}

Working with multiple tabs with Socket.io

I've the following code working in my server-side, it's all ok. But, I want to keep the same connection between n tabs, because when I open a new tab, looks like I've disconnected from the first tab... So, how can I keep the same connection?
client.js
socket.emit("connected", {user: inputUser.val()};
app.js
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
users[socket.user] = socket;
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
delete users[socket.user];
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
Update:
The typing event above works fine... So I tried the typing event according to the answer:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
But it is giving the following error:
users[userMsg].emit("typing", {user: socket.user});
^
TypeError: users[userMsg].emit is not a function
Update²:
To fix the typing event error, I just changed to:
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
for(let userSet of users[userMsg]) {
userSet.emit("typing", {user: socket.user});
}
}
});
There is no simple way to share a single socket.io connection among multiple tabs in the same browser. The usual model for multiple tabs would be that each tab just has its own socket.io connection.
The opening of a new tab and a new socket.io connection should not, on its own, cause your server to think anything was disconnected. If your code is doing that, then that is a fault in your code and it is probably easier to fix that particular fault.
In fact, if you want to explicitly support multiple tabs and be able to recognize that multiple tabs may all be used by the same user, then you may want to change your server side code so that it can keep track of multiple sockets for a single user, rather than how it is currently coded to only keep track of one socket per user.
If your server code is really just trying to keep track of which users online, then there's probably an easier way to do that by referencing counting each user. I will post a code example in a bit.
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// increment reference count for this user
if (!users[socket.user]) {
users[socket.user] = 0;
}
++users[socket.user];
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// decrement reference count for this user
// and remove user if reference count hits zero
if (users.hasOwnProperty(socket.user)) {
--users[socket.user];
if (users[socket.user] === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
If you need the users object to have the socket object in it, then you can change what is stored in the users object to be a Set of sockets like this:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token
this.socket = io.connect(`${environment.domain}` , {
query: {token: this.authservice.authToken}
});
then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token
const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];
then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user
if ( user.username in users) {
console.log('already exists')
users[user.username].push(socket);
}
if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)
else {
socket.nickname = username;
users[socket.nickname] = [socket];
}
then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message
socket.on('chat', (data) => {
if (data.to in users) {
for(let i = 0; i < users[data.to].length; i++) {
users[data.to][i].emit('chat', data)
}
for(let i = 0; i < users[data.user].length; i++) {
users[data.user][i].emit('chat', data)
}
}
})
you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem
My solution is joining socket to a room with specific user Id.
io.on('connection', async (socket) => {
socket.join('user:' + socket.handshake.headers.uid) // The right way is getting `uid` from cookie/token and verifying user
})
One advantage is sending data to specific user (sending to all tabs)
io.to('user:' + uid).emit('hello');
Hope it's helpful!
I belive the best way is create a channel for the user and unique it by their ID, so, when you need to receive or send something you use the channel and every socket connected to it will receive.
Another solution is to save the flag to localStorage and use eventListener to change localStorage.
Do not connect when another connection exists.
and save message in local storage for send with master tab.

Websockets: send messages and notifications to all clients except sender

I am developing chat based on websockets and webrtc. I would like to send messages to all connected users except sender but I cannot find suitable solution. To be more specific, I would like to send notifications to other connected users that new user has joined to the chat.
I am trying to give a unique ID to every connected user, but the first assigned ID is re-writed by every new user and I cannot diferentiate users.
Server:
// list of users
var CLIENTS=[];
var id;
// web server is using 8081 port
var webSocketServer = new WebSocketServer.Server({ port: 8081 });
// check if connection is established
webSocketServer.on('connection', function(ws) {
id = Math.random();
CLIENTS[id] = ws;
CLIENTS.push(ws);
ws.on('message', function(message) {
console.log('received: %s', message);
var received = JSON.parse(message);
if(received.type == "login"){
ws.send(message); // send message to itself
/* *********************************************************** */
/* *** Here I trying to check if message comes from sender *** */
sendNotes(JSON.stringify({
user: received.name,
type: "notes"
}), ws, id);
/* *********************************************************** */
}else if(received.type == "message"){
sendAll(message); // broadcast messages to everyone including sender
}
});
ws.on('close', function() {
console.log('user ' + CLIENTS[ws] + ' left chat');
delete CLIENTS[ws];
});
});
function sendNotes(message, ws, id) {
console.log('sendNotes : ', id);
if (CLIENTS[id] !== ws) {
console.log('IF : ', message);
for (var i = 0; i < CLIENTS.length; i++) {
CLIENTS[i].send(message);
}
}else{
console.log('ELSE : ', message);
}
}
function sendAll(message) {
for (var i=0; i < CLIENTS.length; i++) {
CLIENTS[i].send(message); // broadcast messages to everyone including sender
}
}
Client:
loginButton.addEventListener("click", function(){
name = usernameInput.value;
if(name.length > 0){
socket.send(JSON.stringify({
type: "login",
name: name
}));
}
});
function sendData() {
var data = dataChannelSend.value;
var userName = document.getElementById('greetingUser').innerHTML;
socket.send(JSON.stringify({
username : userName, // fetch user name from browser, after login
type : "message",
message : data
}));
}
socket.onmessage = function(message) {
var envelope = JSON.parse(message.data);
switch(envelope.type) {
case "login":
onLogin(envelope);
break;
case "message":
showMessage(envelope);
break;
}
};
I would highly appreciate If you could give me any hint. Thanks
Here is a very simple way of sending to everyone connected except the sender.
Create a broadcast function on your webSocketServer instance that will
take two params.
...
var webSocketServer = new WebSocketServer.Server({ port: 8081 });
...
/*
* method: broadcast
* #data: the data you wanna send
* #sender: which client/ws/socket is sending
*/
webSocketServer.broadcast = function(data, sender) {
webSocketServer.clients.forEach(function(client) {
if (client !== sender) {
client.send(data)
}
})
}
...
// On your message callback.
ws.on('message', function(message) {
...
// Note that we're passing the (ws) here
webSocketServer.broadcast(message, ws);
})
That's it, the broadcast method will send to each connected client
except the one who is sending.
Ok, so we are now storing the CLIENTS in a way that allows us to uniquely identify each client that is connecting, and store arbitrary information about them for later retrieval.
The code below will send the "notes" message to all clients, and THEN add the newly connecting client to the "all clients" list.
SERVER.JS:
var http = require('http'),
Static = require('node-static'),
WebSocketServer = new require('ws'),
// list of users
/*
We are now storing client data like this:
CLIENTS = {
uniqueRandomClientID: {
socket: {}, // The socket that this client is connected on
clientDetails: { // Any details you might wish to store about this client
username: "",
etc: "etc"
}
}
};
So now to get at the socket for a client, it'll be: CLIENTS[uniqueRandomClientID].socket.
Or to show a client's username, it'll be: CLIENTS[uniqueRandomClientID].clientDetails.username.
You might want to write a 'getClientByUsername' function that iterates the CLIENTS array and returns the client with that username.
*/
CLIENTS = {},
// web server is using 8081 port
webSocketServer = new WebSocketServer.Server({ port: 8081 });
// check if connection is established
webSocketServer.on('connection', function(ws) {
console.log('connection is established');
// Now using a randomly generated ID to reference a client. Probably should be better than Math.random :D
var wsID = Math.floor(Math.random() * 1000);
ws.on('message', function(message) {
console.log('received: %s', message);
var received = JSON.parse(message);
if(received.type == "login"){
// If a client with this login name doesnt exist already, its a new client
if(!CLIENTS[wsID]) {
doBroadcast(
{
"newuser": received.name,
type: "notes"
}
);
// Now add this new client to the list
CLIENTS[wsID] = {
socket: ws,
clientDetails: {
username: received.name
}
};
}
} else if(received.type == "message") {
doBroadcast(message); // broadcast messages to everyone including sender
}
});
ws.on('close', function(_event) {
if(CLIENTS[wsID]) {
console.log('user ' + CLIENTS[wsID].clientDetails.username + ' left chat');
delete CLIENTS[wsID];
}
});
/*
* Added this to 'catch' errors rather than just red dump to console. I've never actually done anything with this myself (I *like* red text in my console), but I know this handler should be here :P
*/
ws.on('error', function(_error) {
console.log("error!");
console.log(_error);
});
/*
* Send an object to a client
*
* #param WebSocketClient _to - The client you want to send to (generally an index in the CLIENTS array, i.e CLIENTS["bobsusername123"]
* #param Object _message - A stringifyable JSON object. Complex ones can screw things up, but your basic key/value pairs are usually fine to send.
*/
function doSend(_to, _message) {
_to.send(JSON.stringify(_message));
};
// Added broadcast function to replace sendAll
// Notice how it JSON stringifies the data before sending
/*
* Broadcast a message to all clients
*
* #param Object _message - A stringifyable JSON object. Complex ones can screw things up, but your basic key/value pairs are usually fine to send.
*/
function doBroadcast(_message) {
for(var client in CLIENTS) {
if(!CLIENTS.hasOwnProperty(client)) continue;
doSend(CLIENTS[client].socket, _message);
}
};
});
var fileServer = new Static.Server('.');
http.createServer(function (req, res) {
fileServer.server(req, res);
}).listen(8080, function(){
console.log("Server is listening 8080 port.");
});
console.log("Server is running on 8080 and 8081 ports");
MY CLIENT.JS (for your reference):
var loginButton = document.getElementById("loginbutton"),
usernameInput = document.getElementById("usernameInput");
var SocketClient = function(_uri, _callbacks) {
this.uri = _uri;
this.callbacks = _callbacks;
};
SocketClient.prototype = {
send: function(_message) {
this.socket.send(_message);
},
connect: function() {
try {
this.socket = new WebSocket("ws://" + this.uri);
} catch(e) { return false; }
for(var callback in this.callbacks) {
if(!this.callbacks.hasOwnProperty(callback)) continue;
this.socket["on" + callback] = this.callbacks[callback];
}
return true;
}
};
var socketClient = new SocketClient(
"127.0.0.1:8081",
{
open: function() {
console.log("connected.");
},
message: function(_message) {
console.log("received data:");
console.log(_message);
},
close: function() {
console.log("closed.");
},
error: function(_error) {
console.log("error: ");
console.log(_error);
}
}
);
socketClient.connect();
loginButton.addEventListener("click", function(){
name = usernameInput.value;
if(name.length > 0){
socketClient.send(JSON.stringify({
type: "login",
name: name
}));
}
});
AND THE CLIENT.HTML TO GO WITH IT:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="text" id="usernameInput"/>
<button type="button" id="loginbutton">Login</button>
<script src="client.js"></script>
</body>
</html>
Ive tested this with NWJS v0.12.3 running the server and Firefox on the client.
This should work
const WebSocket = require('ws');
// Websocket variables
const wss = new WebSocket.Server({
port: 3000
});
console.log('Websocket active on port 3000...');
// New WebSocket Connection
wss.on('connection', function connection(ws) {
console.log('new connection')
// On Message Received
ws.on('message', function incoming(message) {
console.log(message)
// Send To Everyone Except Sender
wss.clients.forEach(function(client) {
if (client !== ws) client.send(message);
});
});
});

XMPP web client (using strophe.js) taking time to connect with ejabberd server

I have implemented an XMPP client for JavaScript (using strophe.js), for which I am referring to the book Professional XMPP with JavaScript and jQuery by Jack Moffitt.
The book has a great explanation on working of XMPP JavaScript client using "strophe.js".
With my code, the XMPP web client is taking 6 seconds to connect with the XMPP server (ejabberd using BOSH), which is not desirable for my application.
Can some one explain why this is happening?
My XMPP client code is as below:
var Arthur = {
connection: null,
jid:"20147001#localhost",
password: "XXXX2014",
handle_message: function (message) {
if ($(message).attr('from')) {
if($(message).attr('from').match(/^abcd007#localhost/)){
console.log("inside message received");
var body = $(message).find('body').contents();
var span = $("<span></span>");
body.each(function () {
if (document.importNode) {
$(document.importNode(this, true)).appendTo(span);
console.log(span);
console.log(span.text());
}
});
notiReceived(span.text());
console.log("afte notiReceived executed");
}
if($(message).attr('from').match(/^xyz2014#localhost/)){
console.log("inside message received");
var body1 = $(message).find('body').contents();
var span1 = $("<span></span>");
body1.each(function () {
if (document.importNode) {
$(document.importNode(this, true)).appendTo(span1);
//console.log(span.find('text'));
console.log(span1);
console.log(span1.text());
}
});
notiReceived(span1.text());
console.log("afte notiReceived executed");
}
}
return true;
}
};
function sendPushNotification(to,operation,request_id,message){
if(to=="citizen"){
var myObject = new Object();
myObject.FROM="Executive";
myObject.FUNCTION=operation;
myObject.MESSAGE = message;
myObject.REQUESTID= request_id;
console.log("inside citizen::"+myObject);
var myString = JSON.stringify(myObject);
$(this).val('');
var msg = $msg({to: 'abcd007#localhost', type: 'chat'})
.c('body').t(myString);
console.log("inside keypress data is::"+msg);
Arthur.connection.send(msg);
}
if(to=="technician"){
var myObject1 = new Object();
myObject1.FROM="Executive";
myObject1.FUNCTION=operation;
myObject1.MESSAGE = "Check Request Status";
myObject1.REQUESTID= request_id;
console.log("inside technician:"+myObject1);
var myString1 = JSON.stringify(myObject1);
$(this).val('');
var msg1 = $msg({to: 'xyz2014#localhost', type: 'chat'})
.c('body').t(myString1);
console.log("after msg send to technician"+msg1);
Arthur.connection.send(msg1);
}
};
function connected() {
console.log("inside connected");
Arthur.connection.addHandler(Arthur.handle_message,
null, "message", "chat");
console.log("inside connected");
Arthur.connection.send($pres());
};
function disconnected() {
console.log("disconnected");
Arthur.connection = null;
};
function disconnect(){
Arthur.connection.disconnect();
};
function connectXMPP() {
var conn = new Strophe.Connection(
hosturl.URL+":5280/http-bind");
console.log("inside connection");
conn.connect(Arthur.jid, Arthur.password, function (status) {
if (status === Strophe.Status.CONNECTED) {
connected();
console.log("connected");
} else if (status === Strophe.Status.DISCONNECTED) {
disconnected();
}
});
Arthur.connection = conn;
};
I don't know whether this is the case here, but in general when connections take long to established, the cause is often a host name resolution issue.
For instance, if a host has both an IPv4 and IPv6 address, but there is no IPv6 connectivity yet IPv6 is tried first, then you may get a delay until the IPv6 connection attempt has timed out.
To examine whether domain name resolution is the cause, you might want to take a look at the network traffic using a tool such as Wireshark.

how to prevent new connection on every page refresh in sockjs

So, every time I refresh the page, it seems like sockjs is creating a new connection.
I am saving every message to my mongodb on every channel.onmessage, so if I refresh my page 7 times and send a message, I would save 7 messages of the same content into my mongodb.
This is very problematic because when I retrieve those messages when I go into the chat room, to see the log, I would see bunch of duplicate messages.
I want to keep track of all connections that are 'active', and if a user tries to make another connection, I want to terminate the old one so there is only one connection listening to each message at a time.
How do I do this ?
var connections = {};
//creating the sockjs server
var chat = sockjs.createServer();
//installing handlers for sockjs server instance, with the same url as client
chat.installHandlers(server, {prefix:'/chat/private'});
var multiplexer = new multiplexServer.MultiplexServer(chat);
var configChannel = function (channelId, userId, userName){
var channel = multiplexer.registerChannel(channelId);
channel.on('connection', function (conn) {
// console.log('connection');
console.log(connections);
connections[channelId] = connections[channelId] || {};
if (connections[channelId][userId]) {
//want to close the extra connection
} else {
connections[channelId][userId] = conn;
}
// }
// if (channels[channelId][userId]) {
// conn = channels[channelId][userId];
// } else {
// channels[channelId][userId] = conn;
// }
// console.log('accessing channel! ', channels[channelId]);
conn.on('new user', function (data, message) {
console.log('new user! ', data, message);
});
// var number = connections.length;
conn.on('data', function(message) {
var messageObj = JSON.parse(message);
handler.saveMessage(messageObj.channelId, messageObj.user, messageObj.message);
console.log('received the message, ', messageObj.message);
conn.write(JSON.stringify({channelId: messageObj.channelId, user: messageObj.user, message: messageObj.message }));
});
conn.on('close', function() {
conn.write(userName + ' has disconnected');
});
});
return channel;
};
The way I resolve a problem like yours was with a Closure and Promises, I don't know if that could help you. I let you the code that help me, this is with EventBus from Vertx:
window.Events = (function NewEvents() {
var eventBusUrl = $('#eventBusUrl').val();
var eventBus = null;
return new RSVP.Promise(function(resolve, reject) {
if(!eventBus) {
eventBus = new vertx.EventBus(eventBusUrl);
eventBus.onopen = function eventBusOpened() {
console.log('Event bus online');
resolve(eventBus);
}
eventBus.onclose = function() {
eventBus = null;
};
}
});
}());
And then in other script I call it in this way:
Events.then(function(eventBus) {
console.log("registering handlers for comments");
eventBus.registerHandler(address, function(incomingMessage) {
console.log(incomingMessage);
});
});
I hope this can help you.
Regards.

Categories