User presence in a socket.io 'room' - websocket

I have a system where users connect to a 'room' using Socket.io. Right now, when people load a page, the connection is made, and I can see how many people are connected to the given page (the URL dictating the room).
What I want is to be able to see metadata about the users on the page. I want to somehow know that Bob, Steve and Mary are looking at the page. Right now I only get the internal session Ids.
Server
io.on('connection', function(socket) {
socket.on('viewPage', function(data, fn) {
var roomName = 'page-' + data.url;
socket.join(roomName);
var room = io.sockets.adapter.rooms[roomName];
console.log(Object.keys(room).length + ' users connected');
fn({msg: 'Connected to room ' + roomName})
})
});
Client
var socket = io();
socket = io.connect("http://localhost:5000");
socket.on('connect', function(data) {
socket.emit('viewPage', {url: '/foo/bar'}, function(resp) {
console.log(resp)
});
});
From looking at the Socket.io docs there's join and so on, but I don't see any way of attaching metadata to the client connection.
How would I go about doing this? How can I ensure that I can see a list of connected users on a page?

Here's a very simple and unsafe way of passing metadata from the client to the Server and attaching it to the sockets using middleware. Usually only a session id is passed and the data is fetched on the server but I've cut some corners.
//Client
socket = io.connect("http://localhost:5000", {query: 'name=Bob'});
//Server
io.use(function (socket, next){
var name = socket.handshake.query.name;
socket._name = name;
next();
});
Now you can do:
var room = io.sockets.adapter.rooms[roomName];
for (var key in room){
console.log(room[key]._name);
}
Note you should handle multi-tabbing from the same user so you'd need to display only distinct _name values

Related

Add identifier to websocket

I am using the Node.js ws library, to listen to events in user accounts on a 3rd party API. For each user, I open a websocket to listen to the events in the user's account.
Turns out, the 3rd-party API doesn't provide a userID for each event, so if I have 10 websocket connections to user-accounts, I cannot determine which account an event came from.
I have access to a unique userId prior to starting each of my connections.
Is there a way to append or wrap the websocket connection with the userId identifier, to each connection I make, such that when I receive an event, I can access the custom identifier, and subsequently know which user's account the event came from?
The code below is a mix of real code, and pseudocode (i.e customSocket)
const ws = new WebSocket('wss://thirdparty-api.com/accounts', {
port: 8080,
});
ws.send(
JSON.stringify({
action: 'authenticate',
data: {
oauth_token: access_token,
},
})
);
// wrap and attach data here (pseudocode at top-level)
customSocket.add({userId,
ws.send(
JSON.stringify({
action: 'listen',
data: {
streams: ['action_updates'],
},
})
)
})
// listen for wrapper data here, pseudocode at top level
customSocket.emit((customData) {
ws.on('message', function incoming(data) {
console.log('incoming -> data', data.toString());
})
console.log('emit -> customData', customData);
})
Looking at the socket.io library, the namespace feature may solve for this, but I can't determine if that's true or not. Below is an example in their documentation:
// your application has multiple tenants so you want to dynamically create one namespace per tenant
const workspaces = io.of(/^\/\w+$/);
workspaces.on('connection', socket => {
const workspace = socket.nsp;
workspace.emit('hello');
});
// this middleware will be assigned to each namespace
workspaces.use((socket, next) => {
// ensure the user has access to the workspace
next();
});
I found a solution to this which is fairly simple. First create a message handler function:
const eventHandler = (uid, msg) => {
console.log(`${uid} did ${msg}`);
};
Then, when you create the websocket for the given user, wrap the .on event with the handler:
const createSocketForUser = (uid, eventHandler) => {
const socket = new WebSocket(/* ... */);
socket.onmessage = (msg) => {
eventHandler(uid, msg)
};
return socket;
}

c# SocketIoClientDotNet, node js socket.IO

c# winform tries to send node.js socket through socket.
The client is connected to server, but the socket.emit value and socket.on value do not communicate normally.
I'd like to find a solution to this.
I would like to send this name of client to the server as json type data, receive json type data from the server, read it, and send data back to json.
The data of socket.emit and socket.on are not working properly, so the code has been deleted.
c# code
private void socketLogin(string email, string pw)
{
var socket = IO.Socket("http://localhost:3000/login.html");
socket.On(Socket.EVENT_CONNECT, () =>
{
});
var loginjson = new JObject();
loginjson.Add("email", email);
loginjson.Add("password", pw);
socket.Emit("socketlogin", loginjson.ToString());
socket.On("login", (data) => {
MessageBox.Show(data.ToString());
});
}
node.js Code
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(socket) {
console.log('connection');
socket.on('socketlogin', function(data) {
var testLogin = { 'Login': "success" };
socket.emit('login', data);
});
});
server.listen(app.get('3000'))
in your C# you are making your socket inside a function, but at the end of the function the socket is thrown away because it is only a local variable.
There are many ways to deal with this, but essentially what you want to do is use a thread to handle the socket comms then dispatch things back to your UI thread.

socket.io data from server to client

server.js
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
app.use(express.static(__dirname + '/public'));
io.on('connection', function(client) {
console.log('Client connected...');
client.on('join', function(data) {
console.log(data);
io.emit('messages', 'Hello');
});
});
index.html
<script>
var socket = io.connect('http://localhost:7777');
socket.on('connect', function(data) {
socket.emit('join', 'Hello World from client');
});
socket.on('messages', function(data) {
alert(data);
});
</script>
I tried to implement very basic of Socket.io.
However, data sending from client to server is available but from server to client doesn't work.
In the command running server.js, 'Hello World from client' is printed. However, alert window doesn't work in the web browser.(I've also tried to console.log).
How to solve this?
Editted
I've put server.js codes in the app.get('/', function(req, res)){ ... }
Then, it doesn't work. Why it doesn't work in app.get?
Try this, I hope it works:
io.on('connection', function(client) {
console.log('Client connected...');
client.on('join', function(data) {
console.log(data);
io.emit('join', data); //this code sending data from server to client
});
});
If you're just trying to fetch some data with an Ajax call such as /test, then there is no need to use socket.io. That's just a classic request/response.
app.get('/test', function(req, res) {
// collect your data and then send it as a response
res.json(data);
});
If you're just trying to incorporate data into a web page that is requested, then you can use res.render() with the template engine of your choice (ejs, handlebars, pug, etc...). That would typically look like this:
app.get('/test', function(req, res) {
// collect your data and then pass it to res.render() to render your
// your template using that data
res.render('someTemplateName', data);
});
The main thing that socket.io is useful for is "pushing" data from server to client without a client request. So, if something happened on the server that the client was not aware of and the server wanted to tell the client about it, then socket.io would be used for that. The classic example is a chat app. Person A sends a chat message to the server that is addressed to Person B. The server receives that message and then needs to "push" it to Person B. That would be perfect for an already connected socket.io connection because the server can just push the data directly to the Person B client, something the server can't do with request/response (since there is no request from person B).
If you still think you need socket.io, then please describe exactly what you're trying to do with it (step by step what you're trying to send to the client).
socket.on("message",function (reply_data) {
console.log('inside on message functions ')
console.log(reply_data);
})
please change 'messages' to "message" that worked for me

socket.io emit to default room does not work

When I call emit without the io.emit() all the connected clients receive the message as you would expect. However when trying to send a message to the default room using io.to(socket.id).emit(), the client does not receive the message.
Refering to default room documentation at
http://socket.io/docs/rooms-and-namespaces/#default-room
Using nodejs v4.2.6
Client:
var socket = io();
socket.on('connect', function() {
$.ajax({
url: '/some-path',
type: 'POST',
data: {socket_id: socket.id},
success: function(data) {
console.log(data);
}
});
});
Server:
app.post('/some-path', function(req, res) {
var socketID = req.body.socket_id;
io.to(socketID).emit('message', {'message': socketID});
res.end(socketID);
});
This is the correct solution. It was discovered that the default room socket.id being used by the server to set the default room is not the same as the client socket.id. Please advise.
On the client if you evaluate socket.id you get something like, v4eEdSX5RnDSXjzuAAAA
On the server in, io.on('connection', function(socket){}) if you evaluate socket.id you will get something like, /#v4eEdSX5RnDSXjzuAAAA.
Notice the /# prepended to the client socket.id.
So you have two ways of solving this issue
When calling emit prepend /# to the socket ID
Override the io "connection" listener to set the room to the socket ID without the /# prepended
Selected option two is as follows:
io.on('connection', function(socket) {
socketID = socket.client.id;
socket.join(socketID);
console.log('a user connected ' + socketID);
});

Socket.IO subscribe to multiple channels

I want to build a simple chat room system on top of Socket.IO where user can create a new chat room and then people can start chatting.
This sound simple but as the Socket.IO 0.9.4 I'm running now, after reading a few SO posts together with the document on socket.io, i'm getting more and more confused. So, I hope that someone can provide me with instruction that WORK with 0.9.4:
I need a way to subscribe to a room. The room name is chosen by user. When a message is posted in a room, the user should receive it. How should I write the server code, how should I write the client code?
A user can join multiple rooms simultaneously.
I want another system to send a message to all user in a certain room. This 'another system' send the message through a request handled by express. How would I write that request handler?
This is all pretty straightforward with the socket.io rooms feature. Take a look at the documentation on LearnBoost wiki.
https://github.com/LearnBoost/socket.io/wiki/Rooms
It allows for being connected to multiple rooms over a single socket. I put together a quick test with the following code.
Server
io.sockets.on('connection', function(client){
client.on('subscribe', function(room) {
console.log('joining room', room);
client.join(room);
})
client.on('unsubscribe', function(room) {
console.log('leaving room', room);
client.leave(room);
})
client.on('send', function(data) {
console.log('sending message');
io.sockets.in(data.room).emit('message', data);
});
});
Client
var socket = io.connect();
socket.on('message', function (data) {
console.log(data);
});
socket.emit('subscribe', 'roomOne');
socket.emit('subscribe', 'roomTwo');
$('#send').click(function() {
var room = $('#room').val(),
message = $('#message').val();
socket.emit('send', { room: room, message: message });
});
Sending a message from an Express route is pretty simple as well.
app.post('/send/:room/', function(req, res) {
var room = req.params.room
message = req.body;
io.sockets.in(room).emit('message', { room: room, message: message });
res.end('message sent');
});

Resources