I'm using socket.io for realtime communication. When inetrnet is not available for few seconds, then it will connect to server when the connection is establish and then it will immediately trigger event which will log in my clinet. After establishing connection, (i think) my socket.io on server side fire disconnect event after ~one minute (in callback function I log off my client), and that is my problem.
This is my server side. (a have server.js and he run other.js files, in object 'files' a have that files).
server.js :
io = require('socket.io').listen(server);//server listening on 8080 port
io.configure(function () {
io.set('transports', ['websocket', 'xhr-polling']);
io.set('log level', 1);
io.disable('browser client');
});
var files = {
control: null,
terminal: null,
messages: null
};
for (var game in games) {
games[game] = require('./game_modules/' + game + '.js').listen(io, create_waiter(game));
}
require('http').createServer(function (req, res) {
var resultCode = 500;
var resultText = '';
var parts = require('url').parse(req.url, true);
var matches = parts.pathname.match('^/([a-z_-]+)/([a-z_-]+)/$');
if (matches) {
var game = matches[1];
var command = matches[2];
resultText = games[game][command](parts.query);
resultCode = 200;
res.writeHead(resultCode, {'Content-Type': 'text/plain'});
res.end(resultText);
}).listen(8081, "127.0.0.1");
control.js :
var control = module.exports = function() {
};
control.listen = function(io) {
this.server = io.of('/control');
this.onTerminate = onTerminate;
//********************************************************************************
this.server.on('connection', function(socket) {
socket.on('disconnect', function(data) {
console.log(data); //after my client reconnect, disconnect event is triggered, and
//console.log write 'close timeout'
After client reconnect, I do not want trigger disconnect event, or if disconnect event was triggered I want check in callback function: is my client connected and prevent "log off" for my client.
Thanks :)
Related
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.
I have opened the server.js and the address:http://localhost:8081 on my browser. But then a text "Upgrade Required" appeared at the top left conern of the website.
What is the problem of that? What else do I need to upgrade?
Here is the server.js:
var serialport = require('serialport');
var WebSocketServer = require('ws').Server;
var SERVER_PORT = 8081;
var wss = new WebSocketServer({
port: SERVER_PORT
});
var connections = new Array;
SerialPort = serialport.SerialPort,
portName = process.argv[2],
serialOptions = {
baudRate: 9600,
parser: serialport.parsers.readline('\n')
};
if (typeof portName === "undefined") {
console.log("You need to specify the serial port when you launch this script, like so:\n");
console.log(" node wsServer.js <portname>");
console.log("\n Fill in the name of your serial port in place of <portname> \n");
process.exit(1);
}
var myPort = new SerialPort(portName, serialOptions);
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);
function showPortOpen() {
console.log('port open. Data rate: ' + myPort.options.baudRate);
}
function sendSerialData(data) {
if (connections.length > 0) {
broadcast(data);
}
}
function showPortClose() {
console.log('port closed.');
}
function showError(error) {
console.log('Serial port error: ' + error);
}
function sendToSerial(data) {
console.log("sending to serial: " + data);
myPort.write(data);
}
wss.on('connection', handleConnection);
function handleConnection(client) {
console.log("New Connection");
connections.push(client);
client.on('message', sendToSerial);
client.on('close', function () {
console.log("connection closed");
var position = connections.indexOf(client);
connections.splice(position, 1);
});
}
function broadcast(data) {
for (c in connections) {
connections[c].send(data);
}
}
OK, websockets...
The "upgrade required" status marks the start of a websocket handshake. Normally your client sends this first to the WS server. The server answers in a pretty similar manner (details here : https://www.rfc-editor.org/rfc/rfc6455 ), and then proceed to pipe the actual data.
Here, you're opening a connection from your client as regular http, sending a simple GET. What you see on the screen is the server dumbly proceeding with an already corrupted handshake.
That's not how you open a WS client side connection. You don't usually open WS pages from the browser. It ought to be opened from a JavaScript call, such as new WebSocket(uri). So what you want is a regular http server on another port, that serves a page containing the necessary Javascript to open the actual WS connection and do something useful with its data. You'll find a clean example here : http://www.websocket.org/echo.html
I've written my application and now I've activated New Relic and it pings my app every 30 mins to keep my server alive (heroku). Otherwise before the server would just idling and then taking a lot of time to restart on the first request.
I'm running a socket.io server and the problem that i'm facing now, is that after a lot of time that the server runs, it gets stuck in answering to the namespaces. The application doesn't give any fatal error, it keeps running but just stops answering.
It keeps working as usual (still detects and log new connected clients), but just doesn't sends the messages.
Nothing weird on the logs.
Could you help to check if I'm doing anything really wrong with my code that I shouldn't do and might create problems? (e.g. too many listeners etc.)
I've made a little summary of my code, so there might be some little stupid syntax errors, but the flow should be this one. Everything is repeated by 4, because I have 4 namespaces and 4 different queries.
var app = require('express')();
var http = require('http').Server(app);
var cors = require('cors');
var io = require('socket.io')(http);
var PORT = process.env.PORT || 8080;
var sensorSocket = io.of('/namespace1'); //This gives back all the sensor at once with just now values.
var oldata = [];
var intervalRefresh=300;
app.use(cors());
var mysql = require('mysql');
var connectionsArray = [];
var connection = mysql.createConnection({
host: 'xxx',
user: 'xxx',
password: 'xxx',
database: 'xxx',
port: xx,
dateStrings: 'date'
})
//websockets creation/connection
app.get('/', function (req, res) {
res.sendFile(__dirname + '/client.html');
});
http.listen(PORT, function () {
console.log('\n\n\n\n listening on port: %s', PORT);
});
io.of('/namespace1').on('connection', function (socket) {
newconnectionSensors = 1;
console.log("Client id: %s connected on /sensors", socket.id);
console.log(io.of('/sensors').sockets.length);
socket.on('disconnect', function () {
console.log("Just left the ship grrarr : %s", socket.id);
});
});
io.of('/namespace2').on('connection', function (socket) {
[..Similar to the previous one..]
});
io.of('/namespace3').on('connection', function (socket) {
[..Similar to the previous one..]
});
io.of('/namespace4').on('connection', function (socket) {
[..Similar to the previous one..]
});
//Above here I do the same request every 300ms to the db and if there is any change, sends it into the client.
var CheckDB = setInterval(function (sensorSocket) {
if (io.of('/namespace1').sockets.length > 0) {
var query = connection.query('Query...'),
data = []; // this array will contain the result of our db query
query
.on('error', function (err) {
// Handle error, and 'end' event will be emitted after this as well
console.log(err);
})
.on('result', function (result) {
data.push(result);
})
.on('end', function () {
if ((JSON.stringify(oldata) != JSON.stringify(data)) || newconnection == 1) { //if new data is different than the old data, update the clients
io.of('/namespace1').emit('message', dataSensors, io.of('/namespace1').sockets.id);
newconnection = 0;
oldata = data.slice(0); //copy of data to oldata
}
});
}
}, intervalRefresh);
var CheckDB2 = setInterval(function (sensorSocket) {
[..Similar to the previous one..]
}, intervalRefresh);
var CheckDB3 = setInterval(function (sensorSocket) {
[..Similar to the previous one..]
}, intervalRefresh);
var CheckDB4 = setInterval(function (sensorSocket) {
[..Similar to the previous one..]
}, intervalRefresh);
Found the error.
The real mistake was that I was not having really clear the Non-Blocking concept of NodeJS, so in the code I was not waiting for the answers from the MySQL server and instead I was just throwing queries to the server so the results were going to arrive after hours.
I've fixed by making an escamotage. Basically I added a var (queryexecuting) which is checking if the query has been finish to execute or not.
var CheckDB = setInterval(function (sensorSocket) {
if (io.of('/namespace1').sockets.length > 0 && queryexecuting==0) { //If there are connected clients and there is no query executing.
queryexecuting=1; //Means the query is working
var query = connection.query('Query...'),
data = [];
query
.on('error', function (err) {
console.log(err);
})
.on('result', function (result) {
data.push(result);
})
.on('end', function () {
if ((JSON.stringify(oldata) != JSON.stringify(data)) || newconnection == 1) {
io.of('/namespace1').emit('message', dataSensors, io.of('/namespace1').sockets.id);
newconnection = 0;
oldata = data.slice(0);
}
queryexecuting=0; //Means the query has been finished
});
}
}, intervalRefresh);
please look at the code below. It's a simple program in nodeJS.
Question is why disconnect is not printed? (If you uncomment setTimeout, problem is gone)
What is the real question?: Why can't I start socketIO client and server together and close a socket immediately after connection? What is the know-how regarding connections with socketIO?
"use strict";
var Promise = require("bluebird");
var socketio = require("socket.io");
var socketio_client = require("socket.io-client");
var http = require("http");
var port = 7457;
var hostandport = "http://localhost:" + port.toString();
var server = socketio.listen(port);
var client = socketio_client(hostandport);
server.sockets.on("connect", function (socket) {
console.log("connect");
socket.on("disconnect", function () {
console.log("disconnect");
});
//setTimeout(function() {
client.disconnect();
//}, 1000);
});
You have set up your server incorrectly, do this instead:
var server = require('http').createServer(handler);
var io = require('socket.io')(server);
io.on("connect", function (socket) {
console.log("connect");
socket.on("disconnect", function () {
console.log("disconnect");
});
//More importantly, you have done this part wrong,
//the rest of your code may be functional,
//but it does not adhere to what socket.io sets out in its own API
//(http://socket.io/docs/)
socket.disconnect();
});
In Socket.io there is no such thing as connection on server side and/or browser side. There is only one connection. If one of the sides closes it, then it is closed. So you can close it from Server using socket.disconnect()
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
setTimeout(function() {
socket.disconnect();
}, 1000);
});
Goto http://socket.io/get-started/chat/ for more clarifications.
I am a trying to use socket.io and node.js like this :
The browser sends an event to socket.io, in the data event I call another server to get some data, and I would like to send back a message to the browser using a socket.emit.
This looks like that :
socket.on('ask:refresh', function (socket) {
const net = require("net");
var response = new net.Socket();
response.setTimeout(1000);
response.get_response = function (command, port, host) {
this.connect(port, host, function () {
console.log("Client: Connected to server");
});
this.write(JSON.stringify({ "command": command }))
console.log("Data to server: %s", command);
};
response.on("data", function (data) {
var ret = data.toString();
var tmp_data = JSON.parse(ret.substring(0, ret.length - 1).toString());
var data = new Object();
var date = new Date(tmp_data.STATUS[0].When * 1000 );
data.date = date.toString();
socket.emit('send:refresh', JSON.stringify(data) );
});
response.get_response("version", port, host);
});
};
The thing is that I cannot access "socket.emit" inside response.on.
Could you please explain me how I can put a hand on this ?
Thanks a lot
You appear to be overwriting the actual socket with the one of the callback parameters:
socket.on('ask:refresh', function(socket) {
// socket is different
});
Change the name of your callback variable, and you won't have this problem.