How to get the socket object of each connected user in nodejs? - javascript

I am using socket.io to emit notification for users.
I have a listener that watch for an event.
myEvent.watch ((res,err)=> {
if (!err) {
let id = res.userID;
let msg = res.msg;
//to implement
sendMessage(id, msg);
}
}
And I want to emit a message using socket.io to the concerned user.
I am thinking about storing socket object in memory by userID;
io.on('connection', function (socket) {
// memoryStroage is a key-value sotrage.
memoryStorage.put(socket.id, socket);
socket.on('message', function (data) {
console.log(data);
});
});
and then in the sendMessage service:
function sendMessage(id, msg) {
let socket = memoryStroage.get(id);
socket.emit('new-message', msg);
}
I want to is that a good way ?

You should use socket.io rooms.
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.
So if you have the socket id, you can use:
function sendMessage(id, msg) {
io.to(id).emit('new-message', msg);
}
You can use a custom user id too, using socket.join
io.on('connection', function(socket) {
// memoryStroage is a key-value sotrage.
const userId = getUserId(socket); // Get logged user Id somehow
socket.join(`user-${userId}`);
/* ... */
});
function sendMessage(id, msg) {
io.to(`user-${id}`).emit('new-message', msg);
}

Related

How to make 2 users join the same room in socket.io for 1 to 1 chat?

I was looking at answers here How to send a message to a particular client with socket.io, specifically the 2nd one, which recommends using rooms for 1 to 1 chat. I did the following on my server:
io.on('connection', (socket) => {
socket.on('message', (data) => {
socket.join(data.to);
io.sockets.in(data.to).emit('send_message', { message: data.message, to: data.to });
});
});
And on my client I have this:
const sendMessage = (e) => {
e.preventDefault();
io.emit('message', { to: friend._id, from: currentUserID, message }); // here friend._id
// will be currentUserID in the 2nd user's browser. Which makes it so they join different rooms
setMessage('');
}; // this function runs on form submission.
io.on('send_message', (message) => {
// do stuff with the message
});
But my users won't join the same room. Each one joins a room identified by the other user's id (friend._id). So they're unable to see each other's messages. In the answer from above they do a similar thing, just with each user's emails instead, which should result in the same issue I have as far as I understad.

How to obtain realtime message from socket. io in cordova app?

I am setting up chat functionality in a Cordova project using socket.io
However, I am unable to load the message at realtime.
On the client side of my Cordova app, I have written the code for the send message button as below:
$('#send').click(function() {
if ($('#msg_inp').val()) {
var data = {
fromId: fromId,
toId: toId,
message: $('#msg_inp').val()
}
var child = `<div class="media"><div class="media-body media-color-right">`
+ (data.message) + `</div></div> `
$('#chatbox').append(child);
$('#msg_inp').val('');
socket.emit('chatting',data);
}
}
);
I am emitting the data to the server side to store the message in the database.
The code on the server side is this:
socket.on('chatting', function (data) {
userM.findSenderAndReceiver(data.fromId, data.toId, function (err, result) {
console.log(result);
if (err) {
console.log(err);
throw err;
} else {
userM.insertMessages(data, function (err, result) {
if (err) {
throw err;
console.log(err);
}
});
console.log(data);
data.chattingwith = result.from.username;
data.chattingwithId = result.from.id;
data.from_image_url = result.from.image_url;
data.to_image_url = result.to.image_url;
console.log(data);
socket.broadcast.to(result.to.socketID).emit('chatting', data);
}
});
});
On the server side after storing the message (as you can see from the above code), I am emitting the data to the receiver socketID to display the chat in the receiver's chat window at realtime.
However, I am unable to fetch the broadcasted message on the client side.
I was trying it as :
socket.on('chatting',function(data){
console.log(data) // -> This data doesn't get displayed.
});
So, how should I handle the broadcasted data from the socket to display the message at realtime?

Node.js BinaryServer: Send a message to the client on stream end?

I'm using a node.js BinaryServer for streaming binary data and I want a callback event from the server, after the client calls for the .Stream.end() function.
I can't seem to understand - How can I send a message or some kind of notification when the node.js server actually closes the stream connection ?
Node JS:
server.on('connection', function(client) {
client.on('stream', function (stream, meta) {
stream.on('end', function () {
fileWriter.end();
// <--- I want to send an event to the client here
});
});
});
client JS:
client = new BinaryClient(nodeURL);
window.Stream = client.createStream({ metaData });
....
window.Stream.end();
// <--- I want to recieve the callback message
On the server side, you can send streams to the client with .send. You can send a variety of data types, but a simple string will probably suffice in this case.
On the client side you can also listen to the 'stream' event to receive data back from the server.
Node JS:
server.on('connection', function(client) {
client.on('stream', function (stream, meta) {
stream.on('end', function () {
fileWriter.end();
client.send('finished');
});
});
});
client JS:
client = new BinaryClient(nodeURL);
client.on('stream', data => {
console.log(data); // do something with data
});
window.Stream = client.createStream({ metaData });
....
window.Stream.end();

Socket.io - Know in which room socket is

I am writing a simple chat on socket.io with node.js server. So I need to know in which room socket is. For example, socket has connected and joined a room. So, when this socket sends a message I want to broadcast it only to the sockets that are in that room (sck.broadcast.to(it_room).emit).
io.on("connection", function(sck) {
...
sck.join(availableRoom);
sck.on("message", function(msg) {
sck.broadcast.to(**its room**).emit(msg);
sck.emit(msg);
});
...
});
I do not need an array of sockets and information about them (in which room etc).
You first enter your username and chat room that you join. Once you submit the form, username and room info will be appended URL as query string. in browser: location.search :this is how we reach to query strings. We have to parse it.
add this CDN to your main.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.6.0/qs.min.js"></script>
this cdn for npm querystringify package. since it will be headache to setup npm package in clients side, cdn is easier to handle it. make sure cdn script files should be loaded first before other js files in hour html. once this cdn is loaded we can use:
const { username, room } = Qs.parse(location.search, { ignoreQueryPrefix: true })
{ ignoreQueryPrefix: true } this option will omit the "?" from query string.
once you reached username and room name, we can emit our event from client side.
client.js
socket.emit('join', { username, room }, (error) => {
if (error) {
alert(error)
location.href = '/' //will keep the client in the same url
}
})
now we gotta listen to this event from server side. but we have to keep track of users.
index.js
io.on("connection", socket => {
socket.on("join", ({ username, room }, callback) => {
//we have to define addUser function
let users = [];
const addUser = ({ id, username, room }) => {
// Clean the data
username = username.trim().toLowerCase();
room = room.trim().toLowerCase();
// Validate the data
if (!username || !room) {
return {
error: "Username and room are required!"
};
}
// Check for existing user
const existingUser = users.find(user => {
return user.room === room && user.username === username;
});
// Validate username
if (existingUser) {
return {
error: "Username is in use!"
};
}
// Store user
const user = { id, username, room };
users.push(user);
// users=users.push(user) this will cause error
return { user };
};
//as you see this function will return either error or user object
//Socket generates an id upon connection.
//result is either error or user
const { error, user } = addUser({ id: socket.id, username, room });
if (error) {
return callback(error);
}
socket.join(user.room);//socket joined to this room
//now time to emit a new event
socket.emit("message", ( "Welcome!"));
socket.broadcast
.to(user.room)
.emit(
"message",
(`${user.username} has joined!`)
);
//broadcast will send messages to everyone in the chat room except the connected socket
callback(); //success scenario. let the client knows that it is allowed to connect
})
})
Try with the following, but I am not sure if it works in every situation.
Object.keys(sck.manager.roomClients[sck.id])[1]
I found this by logging socket, then I looked up for rooms information and extract it the way you can see in my code.
I am using 0.9.6 socket.io version, maybe there is a different solution for latest version.

Acknowledgment for socket.io custom event

I am looking for a method to acknowledge a socket.emit call.
socket.emit('message', msg);
I have seen a mechanism where the receiver would send another custom event as an acknowledgement, but this would add thousands of transports in my chat application. Please advice an efficient method.
The third argument to the emit method accepts a callback that will be passed to the server so that you can call in acknowledgement with any data you wish. It's actually really convenient and saves the effort of having paired call-response events.
I'm updating my answer with some code that I just tested.
First on the server side:
io.sockets.on('connection', function (sock) {
console.log('Connected client');
sock.emit('connected', {
connected: 'Yay!'
});
// the client passes 'callback' as a function. When we invoke the callback on the server
// the code on the client side will run
sock.on('testmessage', function (data, callback) {
console.log('Socket (server-side): received message:', data);
var responseData = {
string1: 'I like ',
string2: 'bananas ',
string3: ' dude!'
};
//console.log('connection data:', evData);
callback(responseData);
});
});
On the client side:
console.log('starting connection...');
var socket = io.connect('http://localhost:3000');
socket.on('error', function (evData) {
console.error('Connection Error:', evData);
});
// 'connected' is our custom message that let's us know the user is connected
socket.on('connected', function (data) {
console.log('Socket connected (client side):', data);
// Now that we are connected let's send our test call with callback
socket.emit('testmessage', {
payload: 'let us see if this worketh'
}, function (responseData) {
console.log('Callback called with data:', responseData);
});
});

Categories

Resources