Node JS and Socket IO - javascript

I'm trying to write an object with member functions for receiving and sending data to and from the client with Node JS and Socket IO. The problem is that every time I send data from the client, there is a new 'connection' event that fires. Is that how it works? I thought the connection event would only occur on a new connection. So with the code below, the log echoes 'New connection' with every reception from the client. It doesn't seem right.
io.sockets.on('connection', function (socket) {
console.log('New connection');
var localSocket = socket;
var newsock = new UserSocket(localSocket);
newsock.game = null;
newsock.host = null;
newsock.opponent = null;
newsock.room = "";
newsock.SendToHostSocket('Welcome to Baseball Strategy II');
arrSockets.push(socket);
});
function UserSocket(sock) {
var localSock = sock;
var o = this;
this.sock = localSock;
this.sock.on('clientData', function (data) {
console.log("user socket object");
console.log(o);
o.ParseClientData(data);
});
}
This is a simplified version of what I have for the client:
$(document).ready( function () {
var socket = io.connect('http://local.baseball.com:3333');
function SocketGameOut(outstring) {
socket.emit('clientData', outstring );
}
$("#hostGame").click( function () {
var gamenum = $('input[name=gameSelect]:checked', '#gameForm').val();
var aSendData = { command : "newgame" , senddata : { gameid : gamenum } };
var senddata = JSON.stringify(aSendData);
SocketGameOut(senddata);
});
});

Modify your code as below it worked for me.
//use once.('connection') instead of on.('connection')
io.sockets.once('connection', function (socket) {
console.log('New connection');
var localSocket = socket;
var newsock = new UserSocket(localSocket);
newsock.game = null;
newsock.host = null;
newsock.opponent = null;
newsock.room = "";
newsock.SendToHostSocket('Welcome to Baseball Strategy II');
arrSockets.push(socket);
});

Related

How to keep track of sockets in nodejs?

I am trying to keep track of all tcp connections that are made in my server and store it in an array.
My problem begins when some connection is closed, in this situation I should delete it from my array but I can't do it. Can anyone help me? How do I delete a socket that is stored in my array?
var net = require("net");
function Server(port) {
var self = this;
this.conections = [];
this.server = net.createServer(function(socket) {
self.conections.push(socket); //storing the socket in the array
})
this.server.listen(port);
console.log("TCP Server created on port: "+ port);
}
module.exports = Server;
After thinking for a while I realized that I could give an ID to each socket, this way I can monitor each new connection in an array and delete it in case the connection is lost.
var net = require("net");
function Server(port) {
var self = this;
this.connections = [];
this.connectionsId = 0;
this.server = net.createServer(function(socket) {
socket.id = self.connectionsId++;
self.connections.push(socket); //storing the socket in the array
})
//Event that is always trigger when the connection is ended
socket.on('close', function() {
self.connections = self.connections.filter(function(conn) {
return conn.id !== socket.id;
});
});
this.server.listen(port);
console.log("TCP Server created on port: "+ port);
}
module.exports = Server;
Hope it helps somebody!

Server to server Socket IO messaging

Context:
Node.js running a server with require("socket.io");
Node.js running another server with require("socket.io-client");
Receiving acknowledgements (from the other server) when using the standard :
io.on("connection", function() { console.log("Something connected."); });
Problem:
Unfortunately, I cannot get the CLIENT on either server both are running client & server socket.io to emit a message to the server of the other.
I realize it's probably a code error, so I'll include that for correctness' sake, but for the record I'm wondering if there's something I'm doing wrong semantically as opposed to code-wise.
Code: (LoginServer.js)
var app = require('http').createServer();
var io = require('socket.io')(app);
var ioc = require("socket.io-client");
var LoginServer = (function() {
function LoginServer() {
this.gameServerSocket = ioc.connect("http://localhost:3031", {reconnect: true});
}
LoginServer.prototype.initialize = function() {
var self = this;
io.listen(3032);
io.on("connection", function(socket) {
socket.emit("connection");
});
this.gameServerSocket.on("connection", function() {
console.log("Game Server Connected!");
self.gameServerSocket.emit("login", {accountName: "default"});
});
}
return LoginServer;
})();
exports.LoginServer = LoginServer;
Code: (GameServer.js)
var app = require('http').createServer();
var io = require('socket.io')(app);
var ioc = require("socket.io-client");
var GameServer = (function() {
function GameServer() {
this.loginServerSocket = ioc.connect("http://localhost:3032", {reconnect: true});
this.loginServerSocket.emit("connection");
}
GameServer.prototype.initialize = function() {
var self = this;
io.listen(3031);
io.on("connection", function(socket) {
socket.emit("connection");
});
this.LoginServerResponses();
}
GameServer.prototype.LoginServerResponses = function() {
var self = this;
this.loginServerSocket.on("connection", function() {
console.log("Login Server Connected!");
});
this.loginServerSocket.on("login", function(data) {
console.log("Login Server:", data);
});
}
return GameServer;
})();
exports.GameServer = GameServer;
After doing some more testing, I realized your code is working fine. Maybe you're calling your modules incorrectly? I used this as my index.js and the two server-client pairs connected fine:
// index.js
var GameServer = require('./GameServer.js').GameServer;
var LoginServer = require('./LoginServer.js').LoginServer;
var lserver = new LoginServer();
var gserver = new GameServer();
lserver.initialize();
gserver.initialize();
Side note on sockets
I believe you can accomplish what you want to do with only 1 stream between the login and game servers. Here a redo of your code using only 1 server and 1 client that does the same thing:
GameServer.js:
var http = require('http');
var io = require('socket.io');
function GameServer() { }
GameServer.prototype.initialize = function() {
var self = this;
self.httpServer = http.createServer();
self.ioServer = io(self.httpServer);
self.ioServer.listen(3031);
self.ioServer.on("connection", function(socket) {
socket.emit("connection");
});
this.LoginServerResponses();
}
GameServer.prototype.LoginServerResponses = function() {
var self = this;
this.ioServer.on("connection", function() {
console.log("Login Server Connected!");
});
this.ioServer.on("login", function(data) {
console.log("Login Server:", data);
});
}
exports.GameServer = GameServer;
LoginServer.js:
var ioc = require("socket.io-client");
function LoginServer() {
this.gameServerSocket = ioc.connect("http://localhost:3031", {reconnect: true});
}
LoginServer.prototype.initialize = function() {
var self = this;
self.gameServerSocket.on("connection", function() {
self.gameServerSocket.emit("connection");
});
this.gameServerSocket.on("connection", function() {
console.log("Game Server Connected!");
self.gameServerSocket.emit("login", {accountName: "default"});
});
}
return LoginServer;
exports.LoginServer = LoginServer;
"Unfortunately, I cannot get the CLIENT on either server both are running client & server socket.io to emit a message to the server of the other."
What you really want to do is 1 server, and allow clients to connect.
The server can communicate to the clients, so does the clients to the server, back and forth.
server.js
var app = require('http').createServer();
var io = require('socket.io')(app);
io.listen(3032);
io.on('connection', function(socket) {
console.log('A user(a client) connected!');
// lets welcome this particular client with a nice msg :)
socket.emit('msg group', 'Welcome client! you can emit msgs for me to tell every client in "to everyone please"');
// lets tell everyone someone joined
io.emit('msg group', 'guys someone special joined :)!');
socket.on('to everyone please', function(freedomMsg) {
io.emit('msg group', "a client told me (the server) to send this to everyone (every clients)!: " + freedomMsg);
});
});
aClient.js
var ioc = require("socket.io-client");
var socket = ioc("http://localhost:3032");
// general chat
socket.on('msg group', function(msg) {
console.log(msg);
});
function msgEveryone(message) {
socket.emit('to everyone please', message);
}
msgEveryone('whats up guys?');
I hope that with this you'll understand a bit more on how socket.io (or sockets in general) works :)
Code: (GameServer.js)
first delete these code :
this.loginServerSocket.on("login", function(data) {
console.log("Login Server:", data);
});
second change code:
GameServer.prototype.initialize = function() {
var self = this;
io.listen(3031);
io.on("connection", function(socket) {
socket.emit("connection");
});
this.LoginServerResponses();
}
to
GameServer.prototype.initialize = function () {
var self = this;
io.listen(3031);
io.on("connection", function (socket) {
socket.emit("connection");
socket.on("login", function (data) {
console.log("Login Server:", data);
});
});
this.LoginServerResponses();
}
Try this:
LoginServer.js
var app = require('http').createServer();
var io = require('socket.io')(app);
var ioc = require("socket.io-client");
var LoginServer = (function() {
function LoginServer() {
this.gameServerSocket = ioc.connect("http://localhost:3031", {reconnect: true});
}
LoginServer.prototype.initialize = function() {
var self = this;
io.listen(3032);
io.on("connection", function(socket) {
// a client, maybe game server, is connected
socket.on("login", function(data){ console.log("Credentials: "+ data); });
});
this.gameServerSocket.on("connect", function() {
console.log("Connected to Game Server!");
// Send event to game server
});
}
return LoginServer;
})();
exports.LoginServer = LoginServer;
GameServer.js
var app = require('http').createServer();
var io = require('socket.io')(app);
var ioc = require("socket.io-client");
var GameServer = (function() {
function GameServer() {
this.loginServerSocket = ioc.connect("http://localhost:3032", {reconnect: true});
}
GameServer.prototype.initialize = function() {
var self = this;
io.listen(3031);
io.on("connection", function(socket) {
// a client, maybe login server, is connected
});
this.LoginServerResponses();
}
GameServer.prototype.LoginServerResponses = function() {
var self = this;
this.loginServerSocket.on("connect", function() {
console.log("Connected to Login Server !");
// Try to login
self.loginServerSocket.emit("login", {accountName: "default"});
});
}
return GameServer;
})();
exports.GameServer = GameServer;

How to start a Server-Sent Event "only once" inside a SharedWorker to push message for any open script?

I have a server-sent event (SSE) implementation that is working with almost no issues. The only issue that I am having is "one user can have many connections to the server". Basically, If a user opens more than one web browser's tab, each tab will create a brand new server-sent event request to the server which cause many requests to run from a single user.
To solve this problem, I would like to run the SSE inside a Javascript's SharedWorker.
This means that I have only one SSE communicating with a SharedWorker. Then, every page/web browser will communication with the SharedWorker. This gives me the advantage of only allowing one SSE per user.
This is how my SSE working currently without any type of worker.
$(function(){
//connect to the server to read messages
$(window).load(function(){
startPolling( new EventSource("poll.php") );
});
//function to listen for new messages from the server
function startPolling(evtSource){
evtSource.addEventListener("getMessagingQueue", function(e) {
var data = JSON.parse(e.data);
//handle recieved messages
processServerData(data);
}, false);
evtSource.onerror = function(e) {
evtSource.close();
};
}
});
I would like to have the same setup running. However, I would like to run it inside a javascript's SharedWorker to eliminate having more than one SSE per user.
I am struggling to implement the SharedWorker. Here is what I tried so far
I created a file called worker.js and added this code to it
var ports = [] ;
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
var serv = new EventSource(icwsPollingUrl)
serv.addEventListener("getMessagingQueue", function(e) {
var data = JSON.parse(e.data);
processServerData(data);
}, false);
}
Then on the page where I want to listed to messages I have this code
$(function(){
$(window).load(function(){
var worker = new SharedWorker("worker.js");
worker.port.start();
worker.port.onmessage = function(e) {
console.log(e.data);
console.log('Message received from worker');
}
});
});
What am I missing here?
What am I doing wrong?
How can I correct the implementation?
EDITED
Based on the comments below from #Bergi, here is an updated version of my implementation which is still not posting messages to the connectors. I added comments to my code explaining understanding of what is going on with the code.
On a landing page i.e. index.php I connect to my SharedWorker like this
$(function($){
//establish connection to the shared worker
var worker = new SharedWorker("/add-ons/icws/js/worker1.js");
//listen for a message send from the worker
worker.port.addEventListener("message",
function(event) {
console.log(event.data);
}
, false
);
//start the connection to the shared worker
worker.port.start();
});
This is the code as my worker1.js file contains
var ports = [] ;
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
listenForMessage(event, port);
}
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
listenForMessage = function (event, port) {
port.postMessage("SharedWorker Said: " + event.data);
}
//runs every time and post the message to all the connected ports
function readNewMessages(){
var serv = new EventSource(icwsPollingUrl)
serv.addEventListener("getMessagingQueue", function(e) {
var queue = JSON.parse(e.data);
notifyAllPorts(queue);
}, false);
}
//check all open ports and post a message to each
function notifyAllPorts(msg){
for(i = 0; i < ports.length; i++) {
ports[i].postMessage(msg);
}
}
Here is one more version of my worker1.js
var ports = [] ;
//runs only when a new connection starts
onconnect = function(event) {
var port = event.ports[0];
ports.push(port);
port.start();
//implement a channel for a communication between the connecter and the SharedWorker
port.addEventListener("message",
function(event) {
listenForMessage(event, port);
}
);
}
//reply to any message sent to the SharedWorker with the same message but add the phrase "SharedWorker Said: " to it
listenForMessage = function (event, port) {
port.postMessage("SharedWorker Said: " + event.data);
}
readNewMessages();
//runs every time and post the message to all the connected ports
function readNewMessages(){
console.log('Start Reading...');
var serv = new EventSource(icwsPollingUrl);
serv.addEventListener("getMessagingQueue", function(e) {
var queue = JSON.parse(e.data);
console.log('Message Received');
console.log(queue);
notifyAllPorts(queue);
}, false);
}
//check all open ports and post a message to each
function notifyAllPorts(msg){
for(i = 0; i < ports.length; i++) {
ports[i].postMessage(msg);
}
}
maybe is late but you can create a EventSource singleton in the worker like this:
let ports = [];
var EventSourceSingleton = (function () {
var instance;
function createInstance() {
var object = new EventSource('your path');
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
onconnect = function(e) {
var port = e.ports[0];
ports.push(port);
var notifyAll = function(message){
ports.forEach(port => port.postMessage(message));
}
var makeConnection = function (){
var source = EventSourceSingleton.getInstance();
source.onopen = function (e){
var message = "Connection open"
port.postMessage(message);
}
source.onerror = function(e){
var message ="Ups you have an error";
port.postMessage(message);
}
source.onmessage = function(e){
// var message = JSON.parse(event.data);
notifyAll(e.data);
}
}
port.onmessage = function(e) {
makeConnection();
}
port.start();
}
And you can call it from outside like this.
var shWorker = new SharedWorker('woker.js');
shWorker.port.onmessage = function (e) {
console.log('Message received from worker');
setmsj(e.data);
}
//Dummy message - For initialize
shWorker.port.postMessage(true);
Have fun debuggin this on // chrome://inspect/#workers.

SockJS Client auto reconnect?

I'm trying to figure out a way for my SockJS clients to reconnect to the server if it should go down.
I currently have this:
new_conn = function() {
socket = new SockJS(protocol + serverDomain + '/echo', null, {
'protocols_whitelist': ['websocket', 'xdr-streaming', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling']
});
};
socket.onopen = function () {
clearInterval(recInterval);
};
socket.onclose = function () {
recInterval = window.setInterval(function () {
new_conn();
}, 2000);
};
The problem is that the setInterval keeps firing even after a successful reconnect. It seems that the socket.onopen never gets executed.
Any ideas what I could be doing wrong?
I think it could be related to variable scoping. Try this:
var recInterval = null;
new_conn = function() {
socket = new SockJS(protocol + serverDomain + '/echo', null, {
'protocols_whitelist': ['websocket', 'xdr-streaming', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling']
});
};
socket.onopen = function () {
clearInterval(recInterval);
};
socket.onclose = function () {
recInterval = window.setInterval(function () {
new_conn();
}, 2000);
};
Anyway, it's strange, because you were declaring recInterval on the window object, and it should have worked. If it doesn't work, you could also debug it with a browser, with debugger; statements or interactively by setting local breakpoints... (in onopen, for example).
By the way, I rewrited the whole code like this (I like refactoring:):
var recInterval = null;
var socket = null;
var new_conn = function() {
socket = new SockJS(protocol + serverDomain + '/echo', null, {
'protocols_whitelist': ['websocket', 'xdr-streaming', 'xhr-streaming',
'iframe-eventsource', 'iframe-htmlfile',
'xdr-polling', 'xhr-polling', 'iframe-xhr-polling',
'jsonp-polling']
});
socket.onopen = function () {
clearInterval(recInterval);
};
socket.onclose = function () {
recInterval = setInterval(function () {
new_conn();
}, 2000);
};
};
In case anyone still interested in this topic: The refactored code snippet from franzlorenzon causes a lot of reconnects since it is kind of recursive reconnecting itself somehow since each two seconds a new onclose event is spawned (regardless of the recInterval).
Moving the clear interval right after the socket creation does the trick. I also added a socket = null in the onclose event.
var recInterval = null;
var socket = null;
var new_conn = function() {
socket = new SockJS(protocol + serverDomain + '/echo', null, {
'protocols_whitelist': ['websocket', 'xdr-streaming', 'xhr-streaming',
'iframe-eventsource', 'iframe-htmlfile',
'xdr-polling', 'xhr-polling', 'iframe-xhr-polling',
'jsonp-polling'
]
});
clearInterval(recInterval);
socket.onopen = function() {
};
socket.onclose = function() {
socket = null;
recInterval = setInterval(function() {
new_conn();
}, 2000);
};
};

Socket.io custom client ID

I'm making a chat app with socket.io, and I'd like to use my custom client id, instead of the default ones (8411473621394412707, 1120516437992682114). Is there any ways of sending the custom identifier when connecting or just using something to track a custom name for each ID? Thanks!
You can create an array on the server, and store custom objects on it. For example, you could store the id created by Socket.io and a custom ID sent by each client to the server:
var util = require("util"),
io = require('/socket.io').listen(8080),
fs = require('fs'),
os = require('os'),
url = require('url');
var clients =[];
io.sockets.on('connection', function (socket) {
socket.on('storeClientInfo', function (data) {
var clientInfo = new Object();
clientInfo.customId = data.customId;
clientInfo.clientId = socket.id;
clients.push(clientInfo);
});
socket.on('disconnect', function (data) {
for( var i=0, len=clients.length; i<len; ++i ){
var c = clients[i];
if(c.clientId == socket.id){
clients.splice(i,1);
break;
}
}
});
});
in this example, you need to call storeClientInfo from each client.
<script>
var socket = io.connect('http://localhost', {port: 8080});
socket.on('connect', function (data) {
socket.emit('storeClientInfo', { customId:"000CustomIdHere0000" });
});
</script>
Hope this helps.
To set custom socket id, generateId function must be overwritten. Both of eio and engine props of Socket.io server object can be used for to manage this operation.
A simple example:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.engine.generateId = function (req) {
// generate a new custom id here
return 1
}
io.on('connection', function (socket) {
console.log(socket.id); // writes 1 on the console
})
It seems to be it has been handled.
It must be in mind that socket id must be unpredictable and unique value with considering security and the app operations!
Extra: If socket.id is returned as undefined because of your intense processes on your generateId method, async/await combination can be used to overcome this issue on node.js version 7.6.0 and later. handshake method of node_modules/engine.io/lib/server.js file should be changed as following:
current:
// engine.io/lib/server.js
Server.prototype.generateId = function (req) {
return base64id.generateId();
};
Server.prototype.handshake = function (transportName, req) {
var id = this.generateId(req);
...
}
new:
// function assignment
io.engine.generateId = function (req) {
return new Promise(function (resolve, reject) {
let id;
// some intense id generation processes
// ...
resolve(id);
});
};
// engine.io/lib/server.js
Server.prototype.handshake = async function (transportName, req) {
var id = await this.generateId(req);
...
}
Note: At Engine.io v4.0, generateId method would accept a callback. So it would not needed to change handshake method. Only generateId method replacement is going to be enough. For instance:
io.engine.generateId = function (req, callback) {
// some intense id generation processes
// ...
callback(id);
};
In the newest socket.io (version 1.x) you can do something like this
socket = io.connect('http://localhost');
socket.on('connect', function() {
console.log(socket.io.engine.id); // old ID
socket.io.engine.id = 'new ID';
console.log(socket.io.engine.id); // new ID
});
I would use an object as a hash lookup - this will save you looping through an array
var clients = {};
clients[customId] = clientId;
var lookup = clients[customId];
Do not change the socket IDs to ones of your own choosing, it breaks the Socket.io room system entirely. It will fail silently and you'll have no clue why your clients aren't receiving the messages.
This will work with 2.2.0 and above version of Socket.IO
To set custom Socket Id, generateId function must be overwritten.
A simple example:
Server Side
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.use((socket, next) => {
io.engine.generateId = () => {
// USE ONE OF THESE
socket.handshake.query.CustomId; // this work for me
// return socket.handshake.query.CustomId;
}
next(null, true);
});
io.on('connection', function (socket) {
console.log(socket.id);
})
Clint Side
io.connect(URL, { query: "CustomId = CUSTOM ID IS HERE" })
NOTE: Keep in mind that socket id must be a unique value.
why not a simpler solution that does not need to maintain an array of connected clients and does not override internal socket id?
io.on("connection", (socket) => {
socket.on('storeClientInfo', (data) => {
console.log("connected custom id:", data.customId);
socket.customId = data.customId;
});
socket.on("disconnect", () => {
console.log("disconnected custom id:", socket.customId);
})
});
Client side
let customId = "your_custom_device_id";
socket.on("connect", () => {
socket.emit('storeClientInfo', { customId: customId });
});
or you can override the socket id, like this:
io.on('connection', function(socket){
socket.id = "YOUR_CUSTOM_ID";
});
you can see under the array:
io.sockets.sockets
Can store customId (example userId) in object format instead of for loop, this will improve performance during connection, disconnect and retrieving socketId for emitting
`
var userId_SocketId_KeyPair = {};
var socketId_UserId_KeyPair = {};
_io.on('connection', (socket) => {
console.log('Client connected');
//On socket disconnect
socket.on('disconnect', () => {
// Removing sockets
let socketId = socket.id;
let userId = socketId_UserId_KeyPair[socketId];
delete socketId_UserId_KeyPair[socketId];
if (userId != undefined) {
delete userId_SocketId_KeyPair[userId];
}
console.log("onDisconnect deleted socket with userId :" + "\nUserId,socketId :" + userId + "," + socketId);
});
//Store client info
socket.on('storeClientInfo', function (data) {
let jsonObject = JSON.parse(data);
let userId = jsonObject.userId;
let socketId = socket.id;
userId_SocketId_KeyPair[userId] = socketId;
socketId_UserId_KeyPair[socketId] = userId;
console.log("storeClientInfo called with :" + data + "\nUserId,socketId :" + userId + "," + socketId);
});
`
With this 2.2.0 version of Socket.IO, you can achieve this.
io.use((socket, next) => {
io.engine.generateId = () => socket.handshake.query.token;
next(null, true);
});
If you are trying to use a custom id to in order to communicate with a specific client then you can do this
io.on('connection', function(socket){
socket.id = "someId"
io.sockets.connected["someId"] = io.sockets.connected[socket.id];
// them emit to it by id like this
io.sockets.connected["someId"].emit("some message", "message content")
});

Categories

Resources