Good day guys. Upon solving my problem here in SO. I successfully added user in my array users[] in socket.io and showed the connected users in the client side.
Upon the user disconnection the name of the user will be deleted using this code delete users[socket.user]; but the name of the user remains in the client.
Can you help me on removing the name of the user guys? Thanks.
Here's my server.js
var redis = require('redis');
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
server.listen(8080);
var users = [];
app.get('/', function (req, res) {
res.sendfile(__dirname + '/test.html');
});
io.sockets.on('connection', function (socket) {
socket.on('adduser', function (user) {
socket.user = user;
users.push(user);
updateClients();
});
socket.on('disconnect', function () {
delete users[socket.user];
updateClients();
});
function updateClients() {
io.sockets.emit('update', users);
}
});
And here's my client.html.
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
var socket = io.connect('http://localhost:8080');
var userList = [];
socket.on('connect', function (){
socket.emit('adduser', prompt("What's your name?"));
});
socket.on('update', function (users){
userList = users;
$('#user').empty();
for(var i=0; i<userList.length; i++) {
$('#user').append("<b>" + userList[i] + "</b></br>");
}
});
</script>
<div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
<b>Users</b>
<div id="users">
<p id="user"></p>
</div>
</div>
I don't know what to put in the socket.on('disconnect', function() {}); so the disconnected user and the name of the user will be removed in the client side.
The reason the clients are not updating correctly client-side is because you aren't removing the correctly server-side. You can't delete an array key by its content with delete. Instead, do this:
socket.on('disconnect', function() {
users.splice(users.indexOf(socket.user), 1);
updateClients();
});
The problem with what you're doing is you're effectively doing this:
var users = [];
users.push('foo');
delete users['foo'];
Since arr is an array, users['foo'] will map to undefined. The string foo is actually index one of the array, so you'd have to use delete users[0], which would cause the array key to be still exist but be undefined:
users[0] // undefined
Instead, you should remove the key entirely:
var index = users.indexOf(socket.user);
users.splice(index, 1);
You can't access an array item by content. Write a function to iterate across the users array to remove it.
function removeUser(username){
var users2 = [];
users.forEach(function(user){
if(user === username){ return;} // do nothing this iteration
users2.push(user); // if we haven't returned yet, add to users2
});
users = users2;
return users2;
};
socket.on('disconnect', function() {
removeUser(socket.user);
updateClients();
});
Related
I am trying to have a module contain the information for different roles for a game but whenever I try and receive the data from the variables I create, it comes as undefined and/or I get errors saying something similar to saying the variable inside the data doesn't exist (such as the role's name).
I've seen a bunch of tutorials do similar but I can't seem to figure out what I've done wrong.
My index file.
var express = require('express');
var app = express();
var serv = require('http').Server(app);
var RoleList = require('./_modules/RoleList');
var Socket = function() {}
Socket.list = {};
serv.listen(process.env.PORT || 4567);
console.log("Server started.");
var io = require('socket.io')(serv, {});
io.sockets.on('connection', function(socket) {
socket.id = Math.random();
Socket.list[socket.id] = socket;
console.log('Player Connected');
console.log('Role Name: ' + RoleList.testRole.roleName);
socket.on('disconnect', function() {
delete Socket.list[socket.id];
console.log('Player Disconnected');
});
});
RoleList Module
var Role = function() {
var self = {};
var roleName = 'TestingRoleName';
return self;
}
modules.exports = {
roleTest: Role
}
But upon running the server I get the results.
Server started.
Player Connected
T:undefined
instead of
T:TestingRoleName
Is anyone able to help with this? I'd appreciate that so much :D I am likely doing something completely wrong but I can't seem to find an answer anywhere.
This i because Role is a function not Object. You need to call function to return self. And you should set roleName as property of self
var Role = (function() {
var self = {};
self.roleName = 'TestingRoleName';
return self;
})();
Or you can change Role to this.This is the way I recommend
var Role = {
roleName:'TestingRoleName'
}
I'm building a simple chat using this guide.
When someone login their name is appended to the list of online users. However, each user should not see his/her own name in the list, only the name of the other users. Any suggestions to what I should add/change in my code to fix this?
client-side:
socket.on("update-people", function(data, id){
$("#people").empty();
$.each(data.people, function(a, obj, id) {
$('#people').append("<li class=\"people-item\"><span class="+obj.id+">" + obj.name + "</span></li>");
});
});
socket.on("update-people-withSelf", function(people){
$('#people').append("<li class=\"people-item\"><span class="+people.id+">" + people.name + "</span></li>");
});
server-side:
// Setting up the server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var socket = require("socket.io").listen(server);
var Room = require('./room.js');
var Conversation = require('./conversation.js');
var _ = require('underscore')._;
var uuid = require ('uuid');
server.listen(process.env.PORT || 3000);
console.log('Server is running...');
socket.set("log level", 1);
var people = {};
var rooms = {};
var conversations = {};
var clients = [];
var chatHistory = {};
Array.prototype.contains = function(k, callback) {
var self = this;
return (function check(i) {
if (i >= self.length) {
return callback(false);
}
if (self[i] === k) {
return callback(true);
}
return process.nextTick(check.bind(null, i+1));
}(0));
};
// Gets the html file
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
})
// Gets the css file
app.use(express.static(path.join(__dirname, 'public')));
// When connecting
socket.on("connection", function(client) {
client.on("join", function(name){
client.emit("update-people", {people: people});
sizePeople = _.size(people);
socket.sockets.emit("update-peopleCount", {people: people, count: sizePeople});
var ownerRoomID = inRoomID = null;
roomID = null;
conversationID = null; // This line probably has to go, since users should be able to create several conversations.
people[client.id] = {"name" : name, "owns" : ownerRoomID, "inroom" : inRoomID, "id" : client.id, "room" : roomID, "conversation" : conversationID};
var id = uuid.v4();
//sizePeople = _.size(people);
sizeRooms = _.size(rooms);
sizeConversations = _.size(conversations);
//socket.sockets.emit("update-peopleCount", {people: people, count: sizePeople});
//socket.sockets.emit("update-people", {people: people});
socket.sockets.emit("roomList", {rooms: rooms, count: sizeRooms});
socket.sockets.emit("update-conversations", {conversations: conversations, count: sizeConversations});
client.emit("updateToSelf", "You have connected to the server. Start conversation or create/join room to chat");
client.broadcast.emit('updateToOthers', name + " is online.");
clients.push(client); //populates the clients array with the client object
console.log("Someone joined the chat", people[client.id].id);
//client.broadcast.emit("sendingOwnName", people[client.id].id);
client.broadcast.emit("update-people-withSelf", people[client.id]);
});
If those people have some id assigned to them, then you can have
condition in your loop to not append user him/herself.
if(obj.id !== currentUser.id) { $'#people').append(... }
Of course you can also use user name or some other info.
I found a solution to my problem. I have updated my question with the correct code.
Basically what was needed was an update of users BEFORE the new user is given an id, name ect. Afterwards, that new user broadcast his/her name to the other users.
Im trying to show all the users connected. For first time the event show the only user connected, but if i load a new page
with a new nickname, the users are not updated in the first one, and the second page update perfectly,
but if i load a third page, the third page updates perfectly
and the others not. Only the final page loaded show all the users connected.
Image of my problem
SERVER
var app = require('express')();
var express = require('express');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var nicks = new Array();
app.use(express.static('./public'));
io.sockets.on('connection', function(socket) {
socket.on('nick', function(data) {
var nick = data.nick;
nicks.push(nick);
socket.emit('users', {
nicks: nicks
});
})
});
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
http.listen(3000, function() {
console.log('listening on *:3000');
});
CLIENT
$(document).ready(function() {
var socket = io();
var nickname;
var nicks = new Array();
nickname = prompt('Write your name:');
$("#button").on("click", function(event) {
var nick = nickname;
socket.emit('nick', {
nick: nick
});
});
socket.on('users', function(data) {
nicks = data.nicks;
for (var i = 0; i < nicks.length; i++) {
$("#users_connected").append('<li>' + nicks[i] + '</li>');
}
});
});
The problem is that your are using socket.emit()
from docs:
Emits an event to the socket identified by the string name.
Meaning that the only one that is getting the users event is the same that send a nick event.
Yoy must want to use Server.emit()
From docs:
Emits an event to all connected clients. The following two are
var io = require('socket.io')();
io.sockets.emit('an event sent to all connected clients');
io.emit('an event sent to all connected clients');
That way will allow you to inform all conections about the current users but mostly will fail in your current schema if you dont clear or ignore somehow already added users.
I would prefer to have two events a "new member" and send whole user data not all users and then a "disconected" in order to remove member reference and avoid re printing or validating things.
For more advenced usage you need to check on namespaces. To send events to certain groups of connections.
Check it out ;)
http://socket.io/docs/server-api/
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'm having problems in removing users in Socket.IO, upon their browser close the user in the array users[] is not removed and not updated and i would like also to update the users in the client side. Can you help me remove user programmatically? Here is my code guys, i hope you can help me with my code provided below. I've already surfed the net on how to remove users but i can't really make it work. Thanks in advance guys.
Here is my server.js:
var redis = require('redis');
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var publish = redis.createClient();
var subscribe = redis.createClient();
server.listen(8080);
var users = [];
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.sockets.on('connection', function (socket) {
socket.on('adduser', function (user) {
users[user] = user;
console.log(users);
});
socket.on('disconnect', function (user) {
delete users[user] = user;
io.sockets.emit('update', user);
// delete users[user] = users;
// socket.emit('update', users);
console.log(users)
});
socket.on('update', function () {
users[user] = user;
console.log('Current users: ', users);
});
});
Here is my client.html:
<html>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script>
var socket = io.connect('http://localhost:8080');
socket.on('connect', function(){
socket.emit('adduser', prompt("What's your name?"));
});
</script>
<div style="float:left;width:150px;border-right:2px solid black;height:510px;padding:10px;overflow:scroll-y;text-align:center;">
<b>Users</b>
<div id="users"></div>
</div>
</html>
Thanks again guys, i hope you can help me :)
The server-side disconnect event isn't going to have a user variable in its callback, so you are effectively using delete users[null]. Instead, you should be storing the user variable in the socket-specific data store, like so:
var io = require('socket.io').listen();
var users = [];
io.sockets.on('connection', function(socket) {
socket.on('adduser', function(user) {
socket.set('username', user, function() {
users[user] = user;
});
});
socket.on('disconnect', function() {
socket.get('username', function(err, user) {
delete users[user];
io.sockets.emit('update', users);
});
});
});
When the socket disconnects, you can then find its associated user variable, and then delete it.