Angular 1.5: multiple WebSocket connections - javascript

I'm trying to create a service or factory using Angular 1.5 (with ES6) where I could have multiple instances of it, each one having a different connection to a WebSocket (the main purpose of this is a chat system).
I was able to do a service that works for a single WebSocket connection, but given the purpose of this project, I need to be able to connect to different "rooms" but each one of them has a URL with different connection parameters (like so: ws://localhost:8080/chat/<param1>/<param2>).
I'm using angular-websocket (https://github.com/AngularClass/angular-websocket). Since I'm using ES6 with strict mode, I have to inject $websocket from this lib and create immediately an instance of it on the constructor.
So, what I'm looking for is: being able to create multiple WebSocket connections, ideally in a service/factory, where each one of them has its own connection parameters (which would be given on the controller where this service will be instantiated), and then each instance would be able to manage the sending/receiving of new respective "room" messages.
Using ES5, I could probably create a non-singleton service or factory and that could probably solve this problem, but as I'm learning ES6 I would really love to have this solved that way.
Here's my current Chat service class, which currently only is able to handle a static connection, and it's a singleton.
export default class ChatService {
constructor($websocket) {
'ngInject';
this._$websocket = $websocket('wss://localhost:8080/chat' + '/param1/param2');
this.collection = [];
this.init();
}
init() {
this._$websocket.onMessage(this.onMessage);
this._$websocket.onOpen(this.onOpen);
this._$websocket.onClose(this.onClose);
this._$websocket.onError(this.onError);
}
onOpen() {
console.log('Connection open');
}
onClose(event) {
console.log('Connection closed: ', event);
}
onError(event) {
console.log('Connection Error: ', event);
}
onMessage(message) {
this.collection.push(JSON.parse(message.data));
}
closeSocket() {
this._$websocket.close();
}
sendMessage(text) {
// Code to send a message using this connection
}
}
If you have any other suggestion on how to tackle this problem, I'm all ears.
Thank you.

I use jquery atmosphere js for multi socket connection. You can use it.
Exapmle multi connection code :
HTML
<button id="b1">
ping s1
</button>
<button id="b2">
ping s1
</button>
<div id="s1">
<h1>s1 pings</h1>
<p></p>
</div>
<div id="s2">
<h1>s2 pings</h1>
<p></p>
</div>
JS:
var container;
function Request(sU) {
this.url = sU;
this.contentType = 'application/json';
this.logLevel = 'debug';
this.trackMessageLength = false;
this.enableProtocol = false;
this.enableXDR = true;
this.transport = 'websocket';
this.fallbackTransport = 'sse';
this.reconnectInterval = 5000;
this.connectTimeout = 30000;
this.timeout = 60000;
this.maxReconnectOnClose = 3;
this.isDetail = false;
this.suspend = true;
//this.headers = {device: $rootScope.imageType}
this.onOpen = function(response) {
console.log("connected");
};
this.onReopen = function(response) {};
this.onMessage = function(response) {
$(container).append("<br>" + response.responseBody );
console.log(response)
};
this.onClientTimeout = function(request) {};
this.onReconnect = function(request, response) {};
this.onError = function(response) {};
this.onClose = function(response) {};
};// Request
function SocketConnector(sU) {
return {
request: new Request(sU),
socket: null,
closeSocket: function(aciklama) {
this.socket.close();
}, //closeSocket
connectSocket: function() {
this.socket = $.atmosphere.subscribe(this.request);
} //connectSocket
};
};
var socket1 = new SocketConnector('https://echo.websocket.org');
socket1.connectSocket();
$("#b1").click(function(){
container = "#s1";
socket1.socket.push(Date.now())
});
var socket2 = new SocketConnector('https://echo.websocket.org');
socket2.connectSocket();
$("#b2").click(function(){
container = "#s2";
socket2.socket.push(Date.now())
});
You can write this in any angular service js.
Look at JS Fiddle example.

Related

SOCKET.IO - Usage of socket.id in two different browsers for same logged in user

This is more of a question regarding what to do in the scenario where you want to trigger a socket event for one user, that might be logged into another browser.
I've got a couple of functions that update a users' workstack real-time (in a queue of other workstacks that are assignable by other users); however, if the user is logged into another browser at the same time, and do an update in one browser, it doesn't update in the other (as they have a different socket.id).
I'm not sure what to do with this... I could do it based on the user ID of the person logged in, but at present my socket code does not have scope of any session variables and although there are modules such as session-socket - I'm not sure if that's the right path to go down.
Can anyone advise a way I might approach this please?
If you don't use any cluster you can follow the approach I had in my Miaou chat : I simply set the user as a property of the socket object and I iterate on sockets when necessary. This allow for a few utilitarian functions.
Here's the (simplified) related code.
io.on('connect', function(socket){
...
var userId = session.passport.user;
if (!userId) return die("no authenticated user in socket's session");
...
var shoe = new Shoe(socket, completeUser) // <=== bindind user socket here
// socket event binding here
The Shoe object :
// A shoe embeds a socket and is provided to controlers and plugins.
// It's kept in memory by the closures of the socket event handlers
function Shoe(socket, completeUser){
this.socket = socket;
this.completeUser = completeUser;
this.publicUser = {id:completeUser.id, name:completeUser.name};
this.room;
socket['publicUser'] = this.publicUser;
this.emit = socket.emit.bind(socket);
}
var Shoes = Shoe.prototype;
// emits something to all sockets of a given user. Returns the number of sockets
Shoes.emitToAllSocketsOfUser = function(key, args, onlyOtherSockets){
var currentUserId = this.publicUser.id,
nbs = 0;
for (var clientId in io.sockets.connected) {
var socket = io.sockets.connected[clientId];
if (onlyOtherSockets && socket === this.socket) continue;
if (socket && socket.publicUser && socket.publicUser.id===currentUserId) {
socket.emit(key, args);
nbs++;
}
}
return nbs;
}
// returns the socket of the passed user if he's in the same room
Shoes.userSocket = function(userIdOrName) {
var clients = io.sockets.adapter.rooms[this.room.id],
sockets = [];
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket && socket.publicUser && (socket.publicUser.id===userIdOrName||socket.publicUser.name===userIdOrName)) {
return socket;
}
}
}
// returns the ids of the rooms to which the user is currently connected
Shoes.userRooms = function(){
var rooms = [],
uid = this.publicUser.id;
iorooms = io.sockets.adapter.rooms;
for (var roomId in iorooms) {
if (+roomId!=roomId) continue;
var clients = io.sockets.adapter.rooms[roomId];
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket && socket.publicUser && socket.publicUser.id===uid) {
rooms.push(roomId);
break;
}
}
}
return rooms;
}
// returns the first found socket of the passed user (may be in another room)
function anyUserSocket(userIdOrName) {
for (var clientId in io.sockets.connected) {
var socket = io.sockets.connected[clientId];
if (socket.publicUser && (socket.publicUser.id===userIdOrName||socket.publicUser.name===userIdOrName)) {
return socket;
}
}
}
// closes all sockets from a user in a given room
exports.throwOut = function(userId, roomId, text){
var clients = io.sockets.adapter.rooms[roomId];;
for (var clientId in clients) {
var socket = io.sockets.connected[clientId];
if (socket.publicUser && socket.publicUser.id===userId) {
if (text) socket.emit('miaou.error', text);
socket.disconnect('unauthorized');
}
}
}
Real code
Now, with ES6 based node versions and WeakMap, I might implement a more direct mapping but the solution I described is robust and efficient enough.

socketIO - Mapping a socket to a connection

This question title was really worded incorrectly, but I have no idea how to word it. If anyone wants to edit it, please feel free.
Basically I'm trying to figure out how to get a specific instance of a player via the socket that the data came from, this is going to be used to relay movements through my server for my small 2d project. Currently I have an object that's storing all of the players by the identification number, some people say it's an object, some say it's a hashmap implementation, some say it's an array, whatever the hell it is, I'm using it.
var connectedPlayers = {};
When a connection is created I create a new player like so:
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var userId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+userId, socket);
connectedPlayers[playerId] = player;
Obviously this is just generating random names/identification numbers for now, but that's perfectly fine, this is executed on the socket.on('connection', function() call.
If you need to see the Player.js script it's just a basic prototype script which sends information to all players about the player logging in, this all works properly.
var playerId;
var playerName;
var socket;
var positionX, positionY;
function Player(playerId, playerName, socket) {
this.playerId = playerId;
this.playerName = playerName;
this.socket = socket;
this.positionX = 250;
this.positionY = 250;
socket.emit('login', { playerID: playerId, playerX: this.positionX, playerY: this.positionY, playerName: playerName});
socket.broadcast.emit('player-connected',{ playerID: playerId, playerX: this.positionX, playerY: this.positionY, playerName: playerName} )
}
Player.prototype.getId = function() {
return this.playerId;
};
Player.prototype.getName = function() {
return this.playerName;
};
Player.prototype.getSocket = function() {
return this.socket;
};
Player.prototype.getX = function() {
return this.positionX;
};
Player.prototype.getY = function() {
return this.positionY;
};
My problem is, I need to be able to find out which Player belongs to a socket when the data comes in, because I want to authoritatively send movements to the clients. To do this I need to not give the client any control when it comes to which playerId it contains, because if I let the client tell the server, we could have other players moving at random.
The only way I've thought of is something like this, but it doesn't seem very efficient.
for(var i in connectedPlayers) {
if(conntectedPlayers[i].getSocket() == socket) {
// do stuff
}
}
EDIT: Adding the entire sockets.on('connection') due to a request from comments.
socketIO.sockets.on('connection', function(socket) {
console.log("Connection maid.");
socket.on('login', function(json) {
sqlConnection.query('select * FROM Accounts', function(err, rows, fields) {
if(err) throw err;
for(var row in rows) {
if(rows[row].Username == json.username) {
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+playerId, socket);
connectedPlayers[playerId] = player;
}
}
});
});
socket.on('move-request', function(json) {
socket.emit('move-request', {x: json.x, y: json.y, valid: true});
});
});
You can just add one or more properties to the socket object upon connection:
socketIO.sockets.on('connection', function(socket) {
console.log("Connection maid.");
socket.on('login', function(json) {
sqlConnection.query('select * FROM Accounts', function(err, rows, fields) {
if(err) throw err;
for(var row in rows) {
if(rows[row].Username == json.username) {
var playerId = Math.floor(Math.random()*(50000-1+1)+1);
var player = new Player(playerId, "Guest"+playerId, socket);
connectedPlayers[playerId] = player;
// add custom properties to the socket object
socket.player = player;
socket.playerId = playerId;
}
}
});
});
socket.on('move-request', function(json) {
// you can access socket.playerId and socket.player here
socket.emit('move-request', {x: json.x, y: json.y, valid: true});
});
});
So, now anytime you get an incoming message on a socket, you then have access to the playerId and player. In your code, when a message arrives, the socket variable for this message is in your parent scope and accessible so you can then get the player and playerId.
I've run into this situation a few times when storing "objects" in a C hash. I use a 32 bit hash function specifically tailed for pointers.
I'm not sure what you could use to uniquely identify the socket instance and suggest you see what underlying fields exist for that purpose. If you do find something that uniquely identifies the socket, you can feed that into a hash algorithm to generate a key. Of course, you would need a hash implementation (most likely in JavaSCript from the looks of it). I found this: http://www.timdown.co.uk/jshashtable/.
Good luck!

Store object dynamically in object

This question might sound stupid or a little too simple but I seriously got stuck.
I have a node.js app and need to store clients to be able to send information from a specific client to a specific other client.
var clients = { };
var chat = io.of('/chat')
.on('connection', function(socket) {
socket
.on('ehlo', function(data) {
// mysql queries here etc to get client's data,
// for example session_id, customer name, ...
// got stuck here, need to save the socket in customers object like:
clients.(customer + data.get_customer_id) = { socket: socket };
I later need to be able to access this property from a different scope etc.
Basically I lack an idea of how these abstract methods can be implemented:
clients.addClient({ id: 123, socket: socket});
And later find them by:
x = clients.findById(123);
// x now should be { id: 123, socket: socket}
Well, you can define a clients module first.
//clients.js
"use strict";
var clients = {
_db: []
};
clients.findById = function(id){
for(var i=0; i < this._db.length; i++){
var client = this._db[i];
if(client.id === id){
return client;
}
}
return null;
};
clients.addClient = function(client) {
if(!this.findById(client.id)){
this._db.push(client);
}
};
module.exports = clients;
The way Node.js works it will cache your module after it's initialized. This after you use it for the first time. This means that every time you require it, you will get the same clients object. So now you can do this in a hypothetical module1.js:
//module1.js
var clients = require('./clients');
clients.addClient(newClient);
And later in another module you can gain access to the same data structure:
//module2.js
var clients = require('./clients');
var client = clients.findById(id);
And that's it, you can gain access to your same data structure from different contexts. The point here is that all modules will share the same clients object.

CometD taking more time in pushing messages

I am trying to implement CometD in our application. But it is taking more time compared to the existing implementation in our project. The existing system is taking time in milliseconds where as CometD is taking 2 seconds to push the message.
I am not sure where I am going wrong. Any guidance will help me lot.
My code:
Java script at client side
(function($)
{
var cometd = $.cometd;
$(document).ready(function()
{
function _connectionEstablished()
{
$('#body').append('<div>CometD Connection Established</div>');
}
function _connectionBroken()
{
$('#body').append('<div>CometD Connection Broken</div>');
}
function _connectionClosed()
{
$('#body').append('<div>CometD Connection Closed</div>');
}
// Function that manages the connection status with the Bayeux server
var _connected = false;
function _metaConnect(message)
{
if (cometd.isDisconnected())
{
_connected = false;
_connectionClosed();
return;
}
var wasConnected = _connected;
_connected = message.successful === true;
if (!wasConnected && _connected)
{
_connectionEstablished();
}
else if (wasConnected && !_connected)
{
_connectionBroken();
}
}
// Function invoked when first contacting the server and
// when the server has lost the state of this client
function _metaHandshake(handshake)
{
if (handshake.successful === true)
{
cometd.batch(function()
{
cometd.subscribe('/java/test', function(message)
{
$('#body').append('<div>Server Says: ' + message.data.eventID + ':'+ message.data.updatedDate + '</div>');
});
});
}
}
// Disconnect when the page unloads
$(window).unload(function()
{
cometd.disconnect(true);
});
var cometURL = "http://localhost:8080/cometd2/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
});
})(jQuery);
Comet service class
#Listener("/service/java/*")
public void processMsgFromJava(ServerSession remote, ServerMessage.Mutable message)
{
Map<String, Object> input = message.getDataAsMap();
String eventId = (String)input.get("eventID");
//setting msg id
String channelName = "/java/test";
// Initialize the channel, making it persistent and lazy
bayeux.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Publish to all subscribers
ServerChannel channel = bayeux.getChannel(channelName);
channel.publish(serverSession, input, null);
}
Is there any thing I need to change in server side code.
You have made your channel lazy, so a delay in message broadcasting is expected (that is what lazy channels are all about).
Please have a look at the documentation for lazy channels.
If you want immediate broadcasting don't set the channel as lazy.

Javascript: inheriting encapsulated variables

Edit: Removed higher-level ideas, included problem-specific and less-transferable code.
I implemented my DAL using DAO's. My application hooks in to various databases (mostly for legacy reasons). In order to facilitate efficient and intelligent usage of connections, I use a ConnectionBroker singleton to manage the various connections that may (or may not be) open. This ConnectionBroker is then injected into the DAO's where they can request control of a particular connection object, request new connections, ect.
From an inheritence POV, I'd like something like:
AbstractDbConnection
|-- MongoDbConnection
|-- MsSqlConnection
|-- CouchDbConnection
|-- ...
Where AbstractDbConnection defines an interface, and implements some shared event-based logic.
var EventEmitter = require('events').EventEmitter;
module.exports = function AbstractDbConnection(host, port, database, login, ...) {
// private
var state = StatesEnum.Closed; // StatesEnum = {Open: 0, Closed: 1, ..}; Object.freeze(StatesEnum);
// api that must be overwritten
this.connect = function connect() {throw new ...}
this.disconnect = function disconnect() {throw new ...}
... <more>
this.getState = function() { return state; }
}
AbstractDbConnection.prototype.__proto__ = EventEmitter.prototype;
And then I implement the interface using driver-specific code:
var mssqldriver = require('mssqldriver'), //fictitious driver
AbstractDbConnection = require(__dirname + '/blah/AbstractDbConnection');
module.exports = function MsSqlConnection(host, port, database, login, ...) {
var me = this;
// implement using driver
this.connect = function connect() {...}
this.disconnect = function disconnect() {...}
... <more>
driverSpecificConnection.on('driverSpecificOpenEvent', function() {
me.emit('open'); // relay driver-specific events into common events
state = StatesEnum.Open; // how ??
}
...
}
MsSqlConnection.prototype.__proto__ = new AbstractDbConnection();
But clearly I want to protect the state property from changing inadvertently.
Just listen for the open event in the "abstract" constructor!
var EventEmitter = require('events').EventEmitter;
module.exports = AbstractDbConnection;
var StatesEnum = module.exports.StatesEnum = Object.freeze({
Open: 0, Closed: 1, …
});
function AbstractDbConnection(host, port, database, login, …) {
// private
var state = StatesEnum.Closed;
EventEmitter.call(this);
this.getState = function() { return state; }
this.on('open', function(e) {
state = StatesEnum.Open;
});
}
AbstractDbConnection.prototype = Object.create(EventEmitter.prototype);
// api that must be overwritten
AbstractDbConnection.prototype.connect = function connect() {throw new …};
AbstractDbConnection.prototype.disconnect = function disconnect() {throw new …};
var Mssqldriver = require('mssqldriver'), //fictitious driver
AbstractDbConnection = require(__dirname + '/blah/AbstractDbConnection');
module.exports = MsSqlConnection;
function MsSqlConnection(host, port, database, login, …) {
AbstractDbConnection.call(this);
this.driver = new Mssqldriver(…);
this.driver.on('driverSpecificOpenEvent', this.emit.bind(this, 'open'));
…
}
MsSqlConnection.prototype = Object.create(AbstractDbConnection.prototype);
MsSqlConnection.prototype.connect = function connect() {…};
MsSqlConnection.prototype.disconnect = function disconnect() {…};
You can use the module pattern to do this.
var transport_module = function() {
var mileage = 0; // private
return {
transport : function(distance) {
mileage += distance;
}
};
}
//use it
var car = transport_module(),
boat = transport_module(),
motorcycle = transport_module();
car.transport(10);
boat.transport(5);
motorcycle.transport(20);
The variable mileage is not visible to any other javascript code. Like a private java/C++ class variable. However, I would think about whether you need this kind of protection. I use modules a lot but not for small objects like class instances in java/C++.

Categories

Resources