Random chat with two users at a time (Socket.io) - node.js

I just started learning NodeJS and Socket.io. Until now I have this demo code, from official socket.io site:
http://socket.io/demos/chat/
I am able to get the unique client's ID of each user (socket) which connects, I am still trying to figure out, How can I make my code to only connect with 1 random user at a time when somebody runs the application. I just want to make random chat like Omegle (http://www.omegle.com/).
Only two users should randomly connect and chat with each other till they re-run the app, if they come back they should get connected with someone else who is in the online queue.
What changes do I need to do to have a similar behaviour?
Update
Added Client site code, main.js
$(function() {
var FADE_TIME = 150; // ms
var TYPING_TIMER_LENGTH = 400; // ms
var COLORS = [
'#e21400', '#91580f', '#f8a700', '#f78b00',
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
var $inputMessage = $('.inputMessage'); // Input message input box
var $loginPage = $('.login.page'); // The login page
var $chatPage = $('.chat.page'); // The chatroom page
// Prompt for setting a username
var username;
var connected = false;
var typing = false;
var lastTypingTime;
var $currentInput = $usernameInput.focus();
//Own Global
var room = '';
var socket = io();
function addParticipantsMessage (data) {
var message = '';
if (data.numUsers === 1) {
// message += "there's 1 participant";
// Status Message
message += "Waiting to connect with someone";
} else {
// message += "there are " + data.numUsers + " participants";
//Status message update
message = "You are connected to a stranger! Say Hey!";
}
log(message);
}
// Sets the client's username
function setUsername () {
username = cleanInput($usernameInput.val().trim());
// If the username is valid
if (username) {
$loginPage.fadeOut();
$chatPage.show();
$loginPage.off('click');
$currentInput = $inputMessage.focus();
// Tell the server your username
socket.emit('add user', username);
// Own
socket.emit('login', {'username' : 'Faizan'});
}
}

Although I would close this question because it's too vague, I feel obliged to give you some insight since I worked way too much with websockets in the last years (although not that much with socketio & nodejs). I suppose some simple guide and relevant links could help you. So first,
Kind of relevant intro
You should already know that Socket.io is a WebSocket implementation.
WebSockets (WS) allow server to send data whenever it wants, as long as the connection is still open, as opposed to old way: client querying all the time asking, if there is an update on the server.
You can imagine a woman and a man at the end of a party: "Thanks for tonight, I'd love to repeat it sometimes soon. Would you give me your number?" - asks the old man. "Ughhh, you know what, better give me yours, I promise I will call you!"
If the girl were to give him her number, he'd call a few times a day asking if she'd go somewhere (and she'd reply no). The other way around, she would call him only if she wanted to go and he would go. Of course he would.
I got a bit carried away, but you get the picture. The woman is a server, the guy is a client.
What is important to understand
(Absolute basic, you should know this =>)
When client connect to your server, (s)he should be served a html page and some javascript, which establishes connection to your WS server. In the code you've posted, Express is used as http server. Check this example to see how you should give user html&js.
You'll also notice namespaces and rooms in most of these tutorials. These are used for separating users into subcategories. One server may contain multiple namespaces (by default only one) and each namespace may contain multiple rooms. You probably won't need to bother with namespaces, one is just enough for your case. You will, however, need to understand rooms (more on that later).
Next thing, taken from your code
io.on('connection', function (socket) {
It's important to know, that socket here basically represent one connected client (in one namespace, but possibly in multiple rooms). You can do all sort of stuff with it, most notably:
install event handlers on it (that's what you do when you call socket.on(event, handler(data))
send events to it with socket.emit(event, data)
send broadcast event to all users with socket.broadcast.emit(event, data)
add/remove it to/from room with socket.join(room), socket.leave(room) respectively.
work with it as with an ordinary variable - store it wherever you want and then reuse it
Do you see the definition of numUsers in your code? That's a global variable which is shared with all clients, since nodejs is single-threaded. In the example it is being incremented inside one of the event handlers. Think we could use something like that? YES.
We can define global variable, queue for example. Or Q if you want. Point is, it can be an array used to store sockets, or rather clients, which are not currently in chat room.
At the end of this section I'd like to point out another obvious thing.
io.on('connection', handler); defines an event handler for 'connection' event happening on the io object (WS server). This is triggered each time client makes connection to your WS server (in your case, through javascript ran inside client's browser). Argument to the method is socket and it is this method where you should add event listeners for each client (that you already do in the code, particularly handling events 'new message', 'add user', 'typing', 'stop typing' and 'disconnect').
What events shall you need
That really depends on how complex you want your app to be. In my opinion, the bare minimum would be (note that you can change the event names, but 'disconnect' should stay 'disconnect'):
event name -> data given
Events handled on server side
login -> username (how the user should be called), possibly password if you want to enable registration
message -> text (content of the message being sent)
leave room -> room name
disconnect
Event handled on client side
connect
chat start -> name (second client's name), room (so we can leave it)
chat end -> no data required if you want to allow only one chat at the same time. In case of multiple chats you should also include which chat got closed
disconnect
Last note before we get started
This is only a rough sketch. There are multiple different crossroads along the way and which path you take mostly depends on your idea of the app. If you want to have multiple chats opened at the same time, you'll need to do some modifications. The same goes if you want to have more than two people connected to the same chat. Here I'll describe the simplest case possible, one chat, to people, no registration. Possibly what you want, judging from your post. Could be wrong.
Workflow
User opens your page in their web browser. You serve them html and javascript. The javascript will start new connection to your websocket server. Also, handlers for desired events should be defined at this point.
When the connection is established, this will be happening:
ON SERVER SIDE
io.on('connection', handler) will be fired. Only appropriate handlers for new socket will be installed, not doing anything else at this point.
ON CLIENT SIDE
socket.on('connect', handler) will be fired. Client should at that point have username stored somewhere. If not, no problem. The connection will be alive for quite some time. You can just call socket.emit('login', {'username':name) any time you wish after you are connected (in the example below I set up variable connected, which defaults to false but will be set to true as soon as connection is established.)
After you send login event from client, server registers it and saves it somewhere. Possibilities are endless, in this case I'll create global dictionary which maps socket.id to username. After that, user socket should be either paired with another one or added to queue.
So, if the queue is empty, simply append socket to global variable (it doesn't have to be an array, since we will pair the first available sockets together, however you may want to implement some history of users so they won't get connected to the same person again). If the queue is not empty, we pull one socket out of the Q and add them to the same room. Room name can be random or whatever you want, I'll use (socket1.id+'#'+socket2.id (if you wanted to have more users in one chat, this would have to be changed).
After you add them both, you'll need to notify them that their chat has started and send them the other peer's name. You will emit event 'chat start'.
Clients will catch the event and open new window. After that, whenever user types something and sends it, client emits event 'message' with payload {'message': user_inserted_text}. Server will capture it in the .on('message' handler and broadcast it to the room. Note:
Broadcasting means sending a message to everyone else except for the socket that starts it.
Note: I am really confused about socketio code right now. Look at this and tell me, if socket.rooms is an array or an object (socket.rooms[room] = room; ?? why?)
To avoid dealing with this not-straightforward code, lets create another global object, rooms, which will store the room names for us. We will map socket.id -> roomName there.
So when message comes, we can get name of the room by calling rooms[socket.id]. Then we broadcast the message like this:
socket.broadcast.to(room).emit('message', data);
Where data is what we received from the sender, therefore object {'text': 'some nice message'}. Your peer will then receive it (you won't) and display it (you should display it when you are sending it).
So the chat continues like this for a while, then one of the users decides (s)he wants to leave / chat with somebody else. They will close window and client will emit event 'leave room'. Server will capture it and send to the other party that her/his peer has disconnected. The same should happen if the client disconnects. After everything is closed, add both users to queue (or only one, if the other has disconnected from the server). In my code I will not make sure they won't get paired again. That is for the OP to code (can't be hard).
So, if you read this far, you deserve some actual code. Although I say actual, it's actually untested. But you know, it should work like this.
Some code
Client side
var connected = false;
var username = 'Faizan';
var room = '';
var socket = io('http://localhost');
socket.on('connect', function (data) { // we are connected, should send our name
connected = true;
if (username) socket.emit('login', {'username' : username});
});
socket.on('chat start', function(data) {
room = data.room;
show_chat_window(data.name); // some method which will show chat window
});
socket.on('chat end', function(data) {
hide_chat_window(); // this will close chat window and alert user that the peer ended chat
socket.leave(room); // it's possible to leave from both server and client, hoever it is better to be done by the client in this case
room = '';
});
socket.on('disconnect', function(data) { // handle server/connection falling
console.log('Connection fell or your browser is closing.');
});
var send_message = function(text) { // method, which you will call when user hits enter in input field
if (connected) socket.emit('message', {'text': text});
};
var leave_chat = function() { // call this when user want to end current chat
if (connected) {
socket.emit('leave room');
socket.leave(room);
room = '';
}
};
Server side
Not including initial requires and html/js serving., only global definitions and main io handler.
var queue = []; // list of sockets waiting for peers
var rooms = {}; // map socket.id => room
var names = {}; // map socket.id => name
var allUsers = {}; // map socket.id => socket
var findPeerForLoneSocket = function(socket) {
// this is place for possibly some extensive logic
// which can involve preventing two people pairing multiple times
if (queue) {
// somebody is in queue, pair them!
var peer = queue.pop();
var room = socket.id + '#' + peer.id;
// join them both
peer.join(room);
socket.join(room);
// register rooms to their names
rooms[peer.id] = room;
rooms[socket.id] = room;
// exchange names between the two of them and start the chat
peer.emit('chat start', {'name': names[socket.id], 'room':room});
socket.emit('chat start', {'name': names[peer.id], 'room':room});
} else {
// queue is empty, add our lone socket
queue.push(socket);
}
}
io.on('connection', function (socket) {
console.log('User '+socket.id + ' connected');
socket.on('login', function (data) {
names[socket.id] = data.username;
allUsers[socket.id] = socket;
// now check if sb is in queue
findPeerForLoneSocket(socket);
});
socket.on('message', function (data) {
var room = rooms[socket.id];
socket.broadcast.to(room).emit('message', data);
});
socket.on('leave room', function () {
var room = rooms[socket.id];
socket.broadcast.to(room).emit('chat end');
var peerID = room.split('#');
peerID = peerID[0] === socket.id ? peerID[1] : peerID[0];
// add both current and peer to the queue
findPeerForLoneSocket(allUsers[peerID]);
findPeerForLoneSocket(socket);
});
socket.on('disconnect', function () {
var room = rooms[socket.id];
socket.broadcast.to(room).emit('chat end');
var peerID = room.split('#');
peerID = peerID[0] === socket.id ? peerID[1] : peerID[0];
// current socket left, add the other one to the queue
findPeerForLoneSocket(allUsers[peerID]);
});
});
P.S.
The code above got a bit messy in the end. It can be done better and I encourage you to do better job than I did. Having this material at hand, go through it step by step and try to understand. I think I commented most, if not all of it. Good luck.
Tl;dr
I am not even surprised. Here, read a comic

Related

Custom room Socket.io NodeJS

I am making an application with sockets and the need arises to broadcast information, but only to people who are inside a room.
This is my code from the server.ts
// Dependencies
import express from 'express'
import http from 'http'
import socket from 'socket.io';
import {connect, disconnect, orderChanged} from './sockets/socket';
import {config} from 'dotenv';
config ();
// Main class
export default class server {
_private static instance: server
public app: express.Application
public port: number
http: http.Server private
public io: socket.Server
// Initialize variables and methods
// Singleton pattern implementation
private constructor () {
this.app = express ()
this.port = Number (process.env.SRV_PORT)
this.http = new http.Server (this.app)
this.io = new socket.Server (this.http, {
cors: {
origin: true,
credentials: true
}
})
this.listenSockets ();
}
// Return the instance running Singleton pattern
public static get instance () {
returns this._instance || (this._instance = new Server ())
}
// Method to start the server
start (callback: any) {
this.http.listen (this.port, callback)
}
private listenSockets (): void {
console.log ('Listening Sockets');
this.io.on ('connection', client => {
console.log ('Connected to room', client.rooms, '-', client.id);
// User disconnected
disconnect (client);
connect (client);
});
}
}
Since node starts, an instance is created in DP Singleton and the socket listener is launched
When an operation happens in the database, anywhere in the app, I send it to call and send information to the front-end which is correctly received by the front-end and does what it has to do. Example url / edit-products
import server from '../core/server';
// Socket broadcast, new information
const __id = String (req.headers.id);
const updatedData = await getNewData (__id);
Server.instance.io.emit ('data changed', updatedData);
The problem is that this information is sent indiscriminately to all users connected to the socket. Now, I have a unique ID that brings multiple users together in a MongoDB model. You could use that ID to broadcast only to users with that ID. There is a logic that implies that if the user connects from Mexico, add it to an Array of people in MongoDB, otherwise it will add it to another MongoDB document, then they are two different IDs.
I would love the room to be that ID.
I saw that I could use the socket's join () method, but that function derives from the connected client, not from the server itself. I try to issue the information like this
// Socket broadcast, new information
const __id = String (req.headers.id);
const updatedData = await getNewData (__id);
Server.instance.io.in (updatedData._id) .emit ('data changed', updatedData);
But at no point did I set up that "ROOM". When the user login, he could add it but I don't know how to create a custom room, he tried something like this
const user = await UserModel.find (_data);
Server.instance.io.join (user.channel._id);
But that function within io does not exist.
It exists this way, but it doesn't work for me
Server.instance.io.on ('user-join', (socket: Socket) => {
console.log (plug);
socket.join (uuid);
});
Server.instance.io.emit ('user join');
What could I do?
.join() is a method on an individual socket. That's how you use it as socket.join(roomName). When the first user joins a room, the room is created automatically and other users can also join it. When the last user leaves the room, the room is removed automatically from the server. So, you join a user's socket to a room - you don't join something to the server.
Similarly, when you tried this:
Server.instance.io.on ('user-join', (socket: Socket) => {
console.log (plug);
socket.join (uuid);
});
That doesn't work because you don't listen for incoming messages from a socket on the server (except for the connection message - which introduces the socket object). You listen for incoming client messages on a socket itself:
Server.instance.io.on ('connection', (socket: Socket) => {
socket.on('user-join', () => {
// you will have to find the room name that goes with this socket
socket.join(someRoomName);
});
});
Also, note that this code:
private listenSockets (): void {
console.log ('Listening Sockets');
this.io.on ('connection', client => {
console.log ('Connected to room', client.rooms, '-', client.id);
// User disconnected
disconnect (client);
connect (client);
});
}
looks problematic. Why would you disconnect a client when they connect? You don't show those functions disconnect() and connect() so it's unclear what they actually do - I would guess they keep track of connected clients somehow. If you're just trying to clean up any state that might have been previously left hanging, then you should be doing something like this:
private listenSockets (): void {
console.log ('Listening Sockets');
this.io.on ('connection', client => {
console.log ('Connected to room', client.rooms, '-', client.id);
client.on('disconnect', () => {
// User disconnected
disconnect(client);
});
// user connected now
connect(client);
});
}
You don't have to worry about inaccurate housekeeping on whether a socket is connected or not. You will always get a disconnect event for a socket when it disconnects. This is for two reasons. For a browser window that closes or a page that the user navigates away from, the browser cleans up all objects associated with that page, including the open socket.io connection. This will always close the socket and cause a disconnect event. Second, socket.io uses ping and pong messages to regularly check if an existing connection is still working. If it's not, it will get disconnected. The client may or may not retry to open a new connection depending upon the circumstance. But, any disfunctional connection (one that isn't respond to ping messages) will get closed by the server and a disconnect event will occur for that too. So, those two circumstances make sure that a disconnect event always happens.
Server.instance.io.emit ('data changed', updatedData); The problem is that this information is sent indiscriminately to all users connected to the socket.
This sends to all users connected to your server and is how it was designed.
To send to a single socket, you would use:
socket.emit(...);
where socket is what you're code calls client, the object you get from the connection event.
To send to all sockets who have joined a room, you would use:
io.in(roomName).emit(...)
where io is the socket.io server instance.
And, there are many, many more variations of .emit() depending upon exactly what you're trying to send to.
Now, I have a unique ID that brings multiple users together in a MongoDB model. You could use that ID to broadcast only to users with that ID. There is a logic that implies that if the user connects from Mexico, add it to an Array of people in MongoDB, otherwise it will add it to another MongoDB document, then they are two different IDs. I would love the room to be that ID.
I don't completely follow what you're trying to do, but it seems like inside your connect(client) function, you could just call client.join(uniqueIDForMultipleUsers) and that would create a room with this uniqueID and add this client to that room. In the future, you can send to everyone in that room with io.in(uniqueIDForMultipleUsers).emit(...).
But at no point did I set up that "ROOM". When the user login, he could add it but I don't know how to create a custom room, he tried something like this
You don't create rooms manually. You just use socket.join(roomName) and the socket.io infrastructure automatically creates the room if it doesn't already exist. Similarly when the last socket in a room either leaves the room or disconnects, the room is automatically removed. So you just don't have to manage the room creation or deletion yourself. In fact, a room object is not something you ever deal with directly - it's a housekeeping item inside of the socket.io server that contains a list of sockets that are currently in the room. A socket can be in as many rooms as it wants to be. You use these on the server:
socket.join(roomName); // add a client's socket to a room
socket.leave(roomName); // remove a client's socket from a room
io.in(roomName).emit(...); // broadcast a message to every socket in a room
What is sometimes a bit confusing about the above logic is that socket.join() and socket.leave() are socket methods, but they actually modify a data structure in the server (where the list of rooms/sockets are kept). For whatever reason, that's just how they chose to originally design the API. Logically, it's more like io.join(socket, roomName) since it's modifying something on the server. But, since the socket knows the server object it's part of, they can leave that off and just do socket.join(roomName).

websocket send to specific user nodejs

I am currently creating a websocket server for a mobile front. and in some cases I need to send a json only to a specific user and after several attempts I still haven't managed to get Websocket to work so I can send my json to one client at a time.
i'm using this library : github.com/websockets/ws
To explain my problem i have several products that contain several variables that need to be refreshed in real time. when a user connects to a product he will receive only the json of that product and the other users will receive the json of the other products they are currently on. that's why i want to send a specific json to a user to enable this
I would like to know if any of you know how to fix the problem as I'm starting to block on it.
Thank you very much.
const opp = new WebSocket.Server({port: 10001});
let user = 0;
let lookup = [];
opp.on('connection', function connection(op) {
lookup.push(op.id);
let id = "";
op.on('message', function incoming(message) {
console.log('received: %s', message);
id = message;
query = {
text: "SELECT id,state,users_list_name,user_win,timer_stamp FROM products WHERE id = " + parseInt(id) + " AND circle = 1 ORDER BY CASE WHEN state = \'available\' THEN \'1\' WHEN state = \'soon\' THEN \'2\' WHEN state = \'expired\' THEN \'3\' END",
};
});
client.connect();
const interval = setInterval(function ping() {
client.query(query, (err, res) => {
if (err) {
console.log(err.toString());
console.log(query);
} else {
console.log(lookup);
for (let i = 0; i < lookup.length; i++){
console.log("########################");
lookup[i].send(JSON.stringify(res.rows));
}
}
});
}, 300);
});```
OK. Still trying to understand the actual spec you're shooting for. But, assuming the following (based on your answers to my prior questions):
A client connects using a webSocket.
When they send a message over that webSocket, that message is an id of something that can be looked up in your database and that they want regular updates for.
Those updates for that particular id should be sent only to that specific client that requested it.
If a different client connects and specifies some id, they should get updates for that id only.
When a client sends a new message that specifies a different id, their updates should now be only for that new id.
Updates for the id that one client requested are sent only to that one client (not to the other clients).
If that's what you really want, here's a way to structure that.
const wss = new WebSocket.Server({port: 10001});
// make database connection that all users share
client.connect();
wss.on('connection', function connection(ws) {
// these variables are unique to each ws connection
let interval, query;
// when webSocket closes, stop any current interval timer associated with this webSocket
ws.on('close', function() {
if (interval) {
clearInterval(interval);
}
});
// when we get an id, start querying for updates on that id
ws.on('message', function incoming(id) {
console.log(`received: ${id}`);
query = {
text: "SELECT id,state,users_list_name,user_win,timer_stamp FROM products WHERE id = " + parseInt(id) + " AND circle = 1 ORDER BY CASE WHEN state = \'available\' THEN \'1\' WHEN state = \'soon\' THEN \'2\' WHEN state = \'expired\' THEN \'3\' END",
};
// if interval is not already going, start it
if (!interval) {
interval = setInterval(function() {
client.query(query, (err, res) => {
if (err) {
console.log(err);
console.log(query);
} else {
// send data to just the one client that this timer is for
ws.send(JSON.stringify(res.rows));
}
});
}, 300);
}
});
});
Now, some comments:
Polling the database on a short time interval with a separate polling loop for every single client simply will not scale at all. You will have serious database scale issues. You really need a better design here, but since we don't know the overall requirements and architecture of your application, we don't have enough info to know what to suggest. Probably you want to leverage notifications in a database that tell you when data has changed rather than you polling it on a short interval on behalf of every single client.
I could find no reason for the lookup data structure. Your comments say that you want to send updates to ONE specific client, the one that requested that id. That can be done with ws.send().
This code assumes that the client variable represents a connection to your database that each of the setIntervals for each connected client can all share. That's why that code was moved out of the wss.on('connection', ...) event handler.
I switched to the more common terminology of wss to refer to the server instance and ws to refer to the webSocket for a particular connected client.
ws.send() is how you send to a connected client. I still don't know what you were doing with op.id. Looking at the doc for the ws library, that doesn't appear to be something you can use to send to.
Your code (and this code) creates a separate setInterval() timer for every webSocket client that connects and it uses a very short interval time. This will not scale. At worst, the interval time needs to be lengthened into multiple seconds (depending upon desired target scale). At best, you need to stop polling the database entirely and use some other mechanism in the database for getting notifications when data has been changed.

NodeJS - Response stream

I built a simple API endpoint with NodeJS using Sails.js.
When someone access my API endpoint, the server starts to wait for data and whenever a new data appears, he broadcasts it using sockets. Each client should receive his own stream of data based on his user input.
var Cap = require('cap').Cap;
collect: function (req, res) {
var iface = req.param("ip");
var c = new Cap(),
device = Cap.findDevice(ip);
c.on('data', function(myData) {
sails.sockets.blast('message', {"host": myData});
});
});
The response do not complete (I never send a res.json() - what actually happens is that the browser keep loading - but the above functionality works).
2 Problems:
I'm trying to subscribe and unsubscribe to to this API endpoint from my client (using RxJS). When I subscribe, I start to receive data via sockets - but I can't unsubscribe to the API endpoint (the browser expect the request to be completed).
Each client should subscribe to his own socket room based on the request IP parameter ( see updated code ). Currently it blasts the message to everyone.
How I can create a stream/service-like API endpoint with Sails.js that will emit new data to each user based on his input?
My goal is to be able to subscribe / unsubscribe to this API endpoint from each client.
Revised Answer
Let's assume your API endpoint is defined in config/routes.js like this:
...
'get /collect': 'SomeController.collectSubscribe',
'delete /collect': 'SomeController.collectUnsubscribe',
Since each Cap instance is tied to one device, we need one instance for each subscription. Instead of using the sails join/leave methods, we keep track of Cap instances in memory and just broadcast to the request socket's id. This works because Sails sockets are subscribed to their own ids by default.
In api/controllers/SomeController.js:
// In order for the `Cap` instances to persist after `collectSubscribe` finishes, we store them all in an Object, associated with which socket the were created for.
var caps = {/* req.socket.id: <instance of Cap>, */};
module.exports = {
...
collectSubscribe: function(req, res) {
if (!res.isSocket) return res.badRequest("I need a websocket! Help!");
if (!!caps[req.socket.id]) return res.badRequest("Dude, you are already subscribed.");
caps[req.socket.id] = new Cap();
var c = caps[req.socket.id]; // remember that `c` is a reference to our new `Cap`, not a copy.
var device = c.findDevice(req.param('ip'));
c.open(device, ...);
c.on('data', function(myData) {
sails.sockets.broadcast(req.socket.id, 'message', {host: myData});
});
return res.ok();
},
collectUnsubscribe: function(req, res) {
if (!res.isSocket) return res.badRequest("I need a websocket! Help!");
if (!caps[req.socket.id]) return res.badRequest("I can't unsubscribe you unless you actually subscribe first.");
caps[req.socket.id].removeAllListeners('data');
delete caps[req.socket.id];
return res.ok();
}
}
Basically, it goes like this: when a browser request triggers collectSubscribe, a new Cap instance listens to the provided IP. When the browser triggers collectUnsubscribe, the server retreives that Cap instance, tells it to stop listening, and then deletes it.
Production Considerations: please be aware that the list of Caps is NOT PERSISTENT (since it is stored in memory and not a DB)! So if your server is turned off and rebooted (due to lightning storm, etc), the list will be cleared, but considering that all websocket connections will be dropped anyway, I don't see any need to worry about this.
Old Answer, Kept for Reference
You can use sails.sockets.join(req, room) and sails.sockets.leave(req, room) to manage socket rooms. Essentially you have a room called "collect", and only sockets joined in that room will receive a sails.sockets.broadcast(room, eventName, data).
More info on how to user sails.sockets here.
In api/controllers/SomeController.js:
collectSubscribe: function(req, res) {
if (!res.isSocket) return res.badRequest();
sails.sockets.join(req, 'collect');
return res.ok();
},
collectUnsubscribe: function(req, res) {
if (!res.isSocket) return res.badRequest();
sails.sockets.leave(req, 'collect');
return res.ok();
}
Finally, we need to tell the server to broadcast messages to our 'collect' room.
Note that this only need to happen once, so you can do this in a file under the config/ directory.
For this example, I'll put this in config/sockets.js
module.exports = {
// ...
};
c.on('data', function(myData) {
var eventName = 'message';
var data = {host: myData};
sails.sockets.broadcast('collect', eventName, data);
});
I am assuming that c is accessible here; If not, you could define it as sails.c = ... to make it globally accessible.

node.js socket.io app - How to kick someone from chat room?

So I have chat app in which only two users can be in a room at a time.
Please note that clientInfo is a global variable and anytime a new user joins a room, an object variable is passed to the server with username and room name. Then this code is executed
socket.on('joinRoom', function(userDetails){
clientInfo[socket.id] = userDetails; //variable contains name and room joined.
socket.join(userDetails.room);
}
I added a feature where a user can type 'kick' as the chat message which will call this function:
function kickUser(socket){
socket.leave(clientInfo[socket.id].room);
delete clientInfo[socket.id];
}
the above code works fine but how can i change the function to kick the other user in the room? I tried the code below but I still ended up kicking myself out. I take it that socket.leave() doesn't care about the socket id but it just uses the room name and the person who called the function.
function kickUser(socket){
var info = clientInfo[socket.id];
if (typeof info == 'undefined'){
return;
}
Object.keys(clientInfo).forEach(function(socketId){
if(info.room == clientInfo[socketId].room) {
if (info.name != clientInfo[socketId].name){
socket.leave(clientInfo[socketId].room);
delete clientInfo[socketId];
}
}
});
}
Try implement next idea
In socket.on('connection', function(socket){ ... }); you must identify user name and room for this connection/socket: client can send username/room on connection itself
socket.on('connect', function {
socket.send(JSON.stringify{do: "introduce", name: "User1", room: "dasRoom"})
})
or server can read it in client cookie. Also you can check here user count in room. This info can be stored in global vars.
User can do kick by send specified message by socket, e.g. {do: "kick
, user: "User1"}, or request url. e.g. /kick/User1.
Function Kick must implement logic to find user by name, send goodbay message to victim and disconnect socket. Also server must send User1 leave room to alive user by socket.

Socket io. Disable automatical joining a room identified by socket's id

In docs it is said: "Each Socket in Socket.IO is identified by a random, unguessable, unique identifier Socket#id. For your convenience, each socket automatically joins a room identified by this id."
I am wondering if there is an option to disable such feature.
My solution was:
io.on('connection', function (socket) {
leaveDefRoom(socket);
[...]
}
function leaveDefRoom(socket){
var room = socket.adapter.rooms;
for (var key in room){
if (key.charAt(0) == '/') {
socket.leave(key);
return;
}
}
}
In socket.io. Every time you emit event. socket.io send the event to the client in this room. If you remove user from the room, you cannot send this user messages. Even broadcast will not work.
Anyway, if you really want, you can leave this room, like any other room:
You can change socket.js file and disable this option:
https://github.com/socketio/socket.io/blob/master/lib/socket.js#L289
Socket.prototype.onconnect = function(){
debug('socket connected - writing packet');
this.nsp.connected[this.id] = this;
// You have to remove this line below:
this.join(this.id);
this.packet({ type: parser.CONNECT });
};

Resources