I am brand new to socket.io and trying to create a sort of mmo using it. However I've been running into a few issues. The current one is if someone refreshes it doesn't look like it is disconnecting the previous socket? In my console it shows the user as having connected twice and will create another listening for the same user.
This is what my console looks like after 3 refreshes:
User has joined : a
in getMap fora
user has disconnected
User has joined : a
User has joined : a
in getMap fora
in getMap fora
in getMap fora
in getMap fora
user has disconnected
user has disconnected
User has joined : a
User has joined : a
User has joined : a
client side:
var socket = io();
setUsername();
function setUsername() {
socket.emit('add user', username);
}
socket.on('startMap', function (data) {
socket.emit('getMap');
});
server side:
io.on('connection', function (socket) {
var addedUser = false;
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
addedUser = true;
console.log("User has joined : " + socket.username)
socket.emit('startMap',{username:socket.username});
});//end add user
socket.on('getMap', function(){
console.log("in getMap for" + socket.username);
});
socket.on('disconnect', function(){
console.log("user has disconnected");
});
});
After 11 refreshes for same user:
(node:8808) Warning: Possible EventEmitter memory leak detected. 11 disconnect listeners added. Use emitter.setMaxListeners() to increase limit
(node:8808) Warning: Possible EventEmitter memory leak detected. 11 add user listeners added. Use emitter.setMaxListeners() to increase limit
(node:8808) Warning: Possible EventEmitter memory leak detected. 11 getMap listeners added. Use emitter.setMaxListeners() to increase limit
(node:8808) Warning: Possible EventEmitter memory leak detected. 11 updateMap listeners added. Use emitter.setMaxListeners() to increase limit
you have to listen to the disconnect event in the server side and broadcast accordingly
io.on('connection', function (socket) {
var addedUser = false;
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
addedUser = true;
console.log("User has joined : " + socket.username)
socket.emit('startMap',{username:socket.username});
});//end add user
socket.on('getMap', function(){
console.log("in getMap for" + socket.username);
});
socket.on('disconnect' , function(){
// your action on user disconnect
socket.broadcast.to(socket.chatroom).emit('user disconnect', name);
});
});
Check whenever user disconnected. Then do stuff there, remove from map etc.
let users = new Object(); // Store users
io.on('connection', function(socket) {
socket.on('disconnect', function() {
console.log("User has disconnected: " + users[socket.id])
delete users[socket.id]; // User disconnected, delete from list
});
socket.on('add user', function(username) {
users[socket.id] = username; // Add user to list
console.log("User has joined: " + username);
});
});
Related
I'm doing this code that emits from serverside and listens from clientside, The connection never ends until the client refresh or exits the page but that is not the case, the thing is that the code on clientside doesn't get execute and no error is showing on the logs.
ServerSide Code:
io.on('connection', function(socket) {
console.log("New socket connection");
socket.on('joinApp', ({msg, password}) =>{
let username;
let rooms;
model.findOne({username: msg}, function(err, found){
if(!err){
var checka = false;
if(found){
if(found.password === password){
user = userLogged(socket.id, msg, found.rooms);
username = getUserName(socket.id).username;
rooms = getUserName(socket.id).rooms;
console.log(msg + " Has entered the chat");
console.log(socket.id);
checka = true;
}else{
console.log("Incorrect password");
}
}else{
console.log("User not found");
}
io.to(socket.id).sockets.emit("checker", checka);
}
});
socket.on('myRooms', () =>{
let user = getUserName(socket.id);
let username = user.username;
let rooms = user.rooms;
if(user.rooms.length === 0){
console.log(username + " You have no rooms");
socket.emit("yourRooms", {username, rooms});
}else{
}
});
socket.on('disconnect', () => {
console.log("Disconnected");
});
});
});
This is the code i have on my express js file, if you see here:
io.to(socket.id).emit("yourRooms", {username, rooms});
The line above is where the problem is, i am emitting the signal that i'm going to listen clientside but the client don't listen to it (Clientside code below):
let title = document.getElementById("title");
let rooms = document.getElementById("rooms");
console.log(socket.id);
socket.on("yourRooms", (username, rooms) => {
if(rooms.length === 0){
rooms.innerHTML = "You have no rooms";
}
title.innerHTML = user + "'s rooms";
});
This part of the code will print the exact socket.id of the client so no problem here.
console.log(socket.id);
I'm not declaring another socket instance since that way it didn't work
I'm using this exact approach in another part of my code and it is working, it's in the file where I created my socket instance:
const socket = io();
I've been trying for hours, no error in the logs and the socket.id is correct when I console.log in the clientside script where I'm having problems, so I don't know what am I doing wrong.
You are emitting an object in express
io.to(socket.id).emit("yourRooms", {username, rooms});
And you are listening for different arguments in client side
socket.on("yourRooms", (username, rooms) => {
Try changing it to
socket.on("yourRooms", ({username, rooms}) => {
So I want to send monster damage the same for all users that connects to the game. The problem at the moment I believe that my code is in io.on('connection', function (socket) { and if there are two users joined at different times the damage will be send two times for both users and it will be different like 53, 24. I need to achieve that no matter when player joins the game the damage should be sent only one time and it has to be the same for all players. Right now I'm sending it every 5 seconds from the server. What I'm thinking to do is storing timestamp with damage in database and send it to all users. Or maybe I can achieve that with the code that I have at the moment ?
io.on('connection', function (socket) {
setInterval(() => {
monsterDamage = Math.floor(Math.random() * monsters[defeats.defeats].damageB) + monsters[defeats.defeats].damageA;
User.findOne({username: username, ingame: true}).then((user) => {
if(!user) {
return console.log('User not found');
}
user.hp -= monsterDamage;
if(user.hp < 0 || user.hp === 0) {
user.hp = 0;
user.ingame = false;
console.log('You LOST');
}
io.emit('monster-attack', {
damage: monsterDamage
});
console.log(monsterDamage);
user.save();
});
}, 5000);
});
Keep in mind that this code is in io.on('connection', function (socket) {. So I believe if theres two players, two connections two damages has been sent or I'm wrong ?
This is a design problem. You have some users and they may connect to your server and you want to inform connected users in something happens. First take setInterval out of io.on('connection'.
In io.on('connection' keep track of users for example in an array (remember to remove them when the connection is closed). Now you have an updated list of all connected users inside your global array.
In setInterval part send message to members of that array.
Code sample:
const users = []
// io defined
setInterval(() => {
// iterate users array and notify them of event
})
io.on('connection', (socket) {
// update users array for example: users.push(socket)
})
io.on('disconnect', (socket) {
// update users array. Remove disconnected user from array.
})
I want to create a live order page where clients can see the status of their order.
For that reason I want to run a function every 10 seconds that checks the SQL database if the order is ready.
function checkOrder(socket, userid, checkinterval) {
pool.getConnection(function(err, connection) {
// Use the connection
connection.query('SELECT * FROM orders WHERE user = ' + userid + ' ORDER BY timestamp DESC', function(err, rows) {
var alldone = false;
for (var i = 0; i < rows.length; i++) {
if (rows[i]['status'] == 'completed') {
alldone = true;
} else {
alldone = false;
break;
}
}
socket.emit('order-update', rows);
connection.release();
if (alldone) {
console.log('all done');
socket.emit('execute', '$("#orderstatus").html(\'Done\');');
clearInterval(checkinterval);
}
});
});
}
var express = require('express');
var app = express();
var app = express();
var options = {
key: fs.readFileSync('privkey.pem'),
cert: fs.readFileSync('cert.pem'),
ca: fs.readFileSync("chain.pem")
};
var server = require('https').createServer(options, app);
var io = require('socket.io')(server);
var port = 443;
server.listen(port, function() {
console.log('Server listening at port %d', port);
});
io.on('connection', function(socket) {
socket.on('trackorder', function(userid) {
var checkinterval = setInterval(function() {
checkOrder(socket, userid, checkinterval);
}, 10000);
});
socket.on('disconnect', function() {
clearInterval(checkinterval);
});
});
Now I'm having issues on stopping the function if either the job is completed or the client disconnects.
How could I achieve that? I suppose the clearInterval() would work inside the function since it is passed but there is an issue with the on disconnect event handler. Either checkinterval is undefined or if I define it globally it stops the wrong function.
How can this be done properly?
Your checkInterval variable is out of scope when the disconnect event comes. You need to move its definition up a level.
io.on('connection', function(socket) {
// checkInterval variable is declared at this scope so all event handlers can access it
var checkInterval;
socket.on('trackorder', function(userid) {
// make sure we never overwrite a checkInterval that is running
clearInterval(checkInterval);
checkInterval = setInterval(function() {
checkOrder(socket, userid, checkInterval);
}, 10000);
});
socket.on('disconnect', function() {
clearInterval(checkinterval);
});
});
In addition:
I added a guard against overwriting the checkInterval variable if you ever get the trackorder event more than once for the same client.
You mispelled checkinterval in one place.
As others have said, polling your database on behalf of every single client is a BAD design and will not scale. You need to either use database triggers (so it will tell you when something interesting changed) or have your own code that makes relevant changes to the database trigger a change. Do not poll on behalf of every single client.
You have no error handling in either pool.getConnection() or connection.query().
Instead of that complicated setInterval stuff, just add a small IIFE that calls itself if the result isnt there yet. Some pseudocode:
function checkOrder(socket, userid){
//a variable pointing to the running timer
var timer;
//on error clear
socket.on("disconnect", ()=>clearTimout(timer));
//a small IIFE
(function retry(){
pool.getConnection(function(err, connection) {
//parse & notice socket
if (!alldone) //retry
timer = setTimeout(retry, 1000);
});
})();
}
I would say you're using a bad approach. You should go for push rather than pull.
What I mean is, emit the event when status of order changes. Don't put the burden on your database to hit it frequently for no reason.
On successful change of status, emit the event order_status_update with order id and what is the new status
socket.emit('order_status_update', {order_id: 57, status: 'In Process'});
This way you don't need any kind of loop or setinterval etc. No worries even if client is connected or not, its sockat.io business to take care of it. You will just raise the event.
My socketio collecting users script;
var users = [];
io.sockets.on( 'connection', function( socket ) {
socket.on('new user', function(data) {
socket.nickname = data.nick;
users[socket.nickname] = socket;
});
});
I want to remove the disconnected user. Done this;
socket.on('disconnect', function() {
delete users[socket.nickname];
});
After that, there is no errors. But when I do
console.log(typeof users[socket.nickname]);
it gives me output of object
Also there are still some garbage data left about the user. (socket events listeners, namespace). What is the proper way to remove user completely without leaving any data ?
socket.on('disconnect', function() {
socket.get('nickname', function(err, user) {
delete users[user];
io.sockets.emit('update', users);
});
});
I am using node.js with cluster module in order to create multiprocess socket.io server.
Using this example I have created the following server-client application:
Server:
var cluster = require('cluster');
// Start master process
if (cluster.isMaster) {
// Create slave workers (in this example there is only one)
var worker = cluster.fork();
// Starts a server that will delegate the connection to the worker
var routerServer = net.createServer(function(connection) {
worker.send({type: 'new_connection'}, connection);
});
routerServer.listen(443, "my-ip-address");
} else {
// Start a server on random Port
var slaveHttp = require("http").Server();
slaveHttp.listen(0, "localhost");
// Connect socket.io to the server
var io = require('socket.io')(slaveHttp);
io.on('connection', function (socket) {
console.log("Connected");
});
// On server messsage (new connection) emit it to the server
process.on('message', function(message, handle) {
if (message.type == 'new_connection') {
slaveHttp.emit('connection', handle);
};
});
}
Client:
var io = require('socket.io-client');
function connect(index) {
connection = io.connect("http://my-ip-address", {
reconnection: true,
reconnectionDelay: 1000,
timeout: 3000,
multiplex: false });
connection.on('connect', function (socket) {
console.log(index + ': Connected');
});
connection.on('connect_error', function (e) {
console.log(index + ': Connection Error: ' + e);
});
connection.on('message', function () {
console.log(index + ': Server message');
});
connection.on('disconnect', function () {
console.log(index + ': disconnect');
});
}
for (var i = 0; i < 10; i++) {
setTimeout(function(index) {
return function() { connect(index); }
}(i), i * 5000);
};
The problem is that some of the clients in the above example managed to connect to the server and exchange messages, but the others failing on timeout, weirder then that I can see in the server's console that it received the connections for the timed-out clients but for some reason they don't succeed to communicate.
If I replace the server code to be on the same process this code runs perfectly.
Can anyone help me?
I found out this is a bug in Node.js, the problem is that there is auto-read machanism in Node.js sockets that starts to read out of every new socket automatically.
When we pass the socket to the child process it can either been read already or not, under heavy load there is more change that the socket will be read by the master (which of course is cuasing the problem), therefore we should explicitly ask to stop reading from this socket.
The following line is a nice workaround (should be added just before worker.send call):
connection._handle.readStop();
Source: https://github.com/joyent/node/issues/7905