so I'm emitting an event from my client that sends a string containing the socket/roomID I'm trying to connect to in it. When the data is received, it's handled by this code on the server side:
const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const port = process.env.PORT || 3000;
app.use(express.static(__dirname + '/public'));
//runs when the user connects to the server
function onConnection(socket){
console.log('a user connected');
// socket.join('some room');
console.log('Created Room: ' + socket.id);
socket.emit("RoomID", socket.id);
socket.on("location", (receivedString) => {
// socket.emit("RoomID", socket.id);
socket.to(socket.id).emit("redirectLocation", receivedString);
console.log('\nReceived the following data from socket "' + socket.id + '": ' + receivedString);
console.log('The amount of people in this room is ' + io.sockets.adapter.rooms.get(socket.id).size + '.');
});
//runs when the user is trying to join a specific room
socket.on("joinRoom", (roomIdentifier) => {
console.log('The room identifier is: ' + roomIdentifier);
console.log('The room is already in the rooms array: ' + io.sockets.adapter.rooms[String(roomIdentifier)]);
if (io.sockets.adapter.rooms[roomIdentifier]) {
socket.join(roomIdentifier);
} else {
console.log(socket.id + 'tried to join ' + roomIdentifier + 'but the room does not exist.');
};
});
socket.on('disconnect', onDisconnection);
}
//runs when the user disconnects from the server
function onDisconnection(socket){
console.log('a user disconnected');
}
//runs when the socket manager registers a connection
io.on('connection', onConnection);
//Runs when a new room was created
io.of("/").adapter.on("create-room", (room) => {
console.log(`room ${room} was created`);
});
io.of("/").adapter.on("join-room", (room, id) => {
console.log(`socket ${id} has joined room ${room}`);
});
http.listen(port, () => console.log('listening on port ' + port));
I don't know why it's coming out as undefined--I thought maybe it was because the rooms dictionary wasn't supposed to have a string or something as a key but I'm really not sure.
There are two problems in the code shared:
Check whether a room already exists
The following command shows that io.sockets.adapter.rooms is a JavaScript Map object.
console.log(io.sockets.adapter.rooms)
// returns [object Map]
To check whether the Map contains the room name, the Map.has(key) should then be used, instead of Map[key]:
io.sockets.adapter.rooms.has(roomIdentifier)
Once the room existence checks are adapted as above, the code still fails to let users join a room, as explained below.
Joining a room
The if statement lets the user join a room only if the room already exists, and does nothing otherwise. As a consequence, rooms are never created, and hence no user can ever join any of them.
if (io.sockets.adapter.rooms.has(roomIdentifier)) {
socket.join(roomIdentifier);
} else {
console.log(socket.id + 'tried to join ' + roomIdentifier + 'but the room does not exist.');
// Socket.join is not executed, hence the room not created.
};
socket.join should be executed outside of the if statement. The if statement can probably be removed from the code, unless some specific action needs to be taken depending on it.
if (io.sockets.adapter.rooms.has(roomIdentifier)) {
// action when room already exists
} else {
console.log(socket.id + 'tried to join ' + roomIdentifier + 'but the room does not exist.');
// action when room is new
};
// join the room in any case
socket.join(roomIdentifier);
Try using io.sockets.adapter.rooms.get(roomId) io.sockets.adapter.rooms returns a Map Object so using io.sockets.adapter.rooms[roomId] returns undefined.
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}) => {
I'm trying to use socket.io to have a client send a message to the server and then the server then sends some info back. Here's what my code looks like
Server Side
let app = express();
let server = app.listen(3000);
app.use(express.static('public'));
let socket = require('socket.io');
let io = socket(server);
let playerData = [[],[],[],[],[]];
io.sockets.on('connection', (socket) => {
console.log(socket.id);
socket.on("joinRoom",(data) =>{
socket.join(data.room);
console.log("recieved join request by socket: " + socket.id + " " + data.class);
let intRoom = parseInt(data.room) // parses what room to join (from string to int)
socket.to(data.room).emit('newJoin', {id: socket.id, class: data.class}); // Tells room that a new user has joined
socket.emit("success", "You have succesfully joined a lobby");
socket.emit('addPlayers', playerData[intRoom]) // Sends player the data of all other players in the room
playerData[intRoom]
playerData[intRoom].push(data.player);
})
})
Client Side
if(enteringRoom){
// This if statement is being run every frame
if(player.loaded){
console.log('telling server to join room'); // This Fires
socket.emit('joinRoom', {room: lobbySelect.value, class: characterSelect.value, player: player});
enteringRoom = false;
}
camera.position.x = 0; // updates the position of a camera
camera.position.z = -50;
}
socket.on('newJoin', (data) => {
console.log('someone has joined the room');
console.log(data);
})
socket.on('success', (res) =>{
console.log(res);
})
socket.on('addPlayers', (data) => {
console.log("Adding Players...");
for(let i = 0; i< data.length; i++){
console.log[i];
console.log("ADDING A PLAYER");
console.log(data[i]);
data[i].loadAssets();
playersInLobby += 1;
}
console.log(playersInLobby);
})
The problem is that nothing in the joinRoom event actually activates on the server end. None of the console logs even go off. The server however is able to console.log the socket.id on connection; that part works. Any ideas why it's not working?
Edit: This also causes the browser tab to freeze up and eventually crash
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);
});
});
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
Problem with Promised Connections
I recently converted my Node app from running on my local machine to utilizing an Amazon EC2 for the Node app and a VPN for the file-serving and MySQL.
I learned just enough about Promises to write the following connection snippet (which runs 3 queries before responding to the client), utilizing Bluebird. The connections worked on my machine, but with the VPN hosted MySQL settings, the connections crashed every time, about 30 seconds after the app started, which I realized was probably because I'd forgotten to close them.
EDIT: Based on the comments, it appears the issue is not in the connection closures.
So I modified my script in the best way I knew to close the connections, but with Promises, this is confusing. This version of the connection doesn't work. It doesn't fail or cause any errors. It just returns no results on the server side. I think my problem is in the way I've closed the connections.
What's causing the issue?
Is it the connection closures?
If so, how would I close them properly?
My (Simplified) MySQL Connection Attempt with Bluebird Promises
var mysql = require('mysql');
var Promise = require('bluebird');
var moment = require('moment');
function createConnection() {
var connection = mysql.createConnection({
dateStrings : true,
host : 'hostname',
user : 'username',
password : 'password',
database : 'database'
});
connection = Promise.promisifyAll(connection);
return connection;
}
function sendGame(req, res, sales, settings, categories, players) {
var game = new Object();
game.sales = sales;
game.players = players;
game.settings = settings;
game.categories = categories;
var JSONgame = JSON.stringify(game);
console.log("Game: " + JSON.stringify(game, undefined, 4));
}
var retrieveSales = Promise.method(function (username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function (results) {
return results[0];
});
});
var retrieveSettings = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving settings...');
var q = 'select * from sales_settings';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrieveCategories = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving categories...');
var q = 'select * from sales_categories';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrievePlayers = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving players...');
var q = 'select * from users';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var gameSucceed = Promise.method(function gameSucceed(req, res) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
//var connection = Promise.promisifyAll(createConnection());
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}).then(function () {
connection.end(),
console.log("...Connection with the MySQL database for Game retrieval ended")
});
});
function getGameData(req, res) {
gameSucceed(req, res).spread(function (sales, settings, categories, players) {
return sendGame(req, res, sales, settings, categories, players);
});
};
var req = new Object();
var res = new Object();
req.body = {
"username" : "user123",
"password" : "password"
}
getGameData(req, res);
Console Result
User user123 retrieving game...
Connection with the MySQL database openned for Game retrieval...
User user123 retrieving sales...
User user123 retrieving settings...
User user123 retrieving categories...
User user123 retrieving players...
...Connection with the MySQL database for Game retrieval ended
Game: {}
var gameSucceed = function gameSucceed(req, res) {
…
var connection = createConnection());
return connection.connectAsync().then(function () {
return Promise.all([…]);
}).then(function () {
connection.end();
});
};
The promise that is ultimately returned from this method does not have a resolution value. It is created by that then call from whose callback you do not return - which will lead to undefined. To fix this, just route the result through:
.then(function(results) {
connection.end();
return results;
});
However, if you do it like that the connection won't be closed in case of an error. The best solution is to use the finally() method, which just works like a finally clause in synchronous code. It's callback will be invoked both for resolutions and rejections, and the resulting promise will automatically carry on the value.
.finally(function() {
connection.end();
})
// .then(function(results) { })
Your code has a particular resource management problem like Bergi put it. You have to keep remembering when to close the collection and when not to.
The optimal solution would be to use Promise.using however, that's only available in the v2 branch of Bluebird so you're going to have to wait a while.
Until then, you can create your own wrapper method that does more basic scoped resource management:
function connect(fn,timeout){
timeout = (timeout === undefined) ? 8000 : timeout; // connection timeout
return createConnection().then(function(connection){
// run the function, when it resolves - close the connection
// set a 7 second timeout on the connection
return fn(connection).timeout(timeout).finally(function(){
connection.end();
});
});
}
Which would let you do:
connect(function(connection){
return gameSucceed(req,resp,connection); // connection is injected to that fn now
}).then(function(val){
// gameSucceed resolution value here
});
Now, when the gameSucceed is done, the connection will close itself automatically. This would make gameSucceed itself look like:
var gameSucceed = Promise.method(function gameSucceed(req, res,connection) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}); // no longer its responsibility to handle the connection
});
Generally, you might also want to consider a more OOPish style of coding for your code.
Good luck, and happy coding.