Why is the client not receiving the socket.io broadcast (rooms)? - javascript

My client is not receiving the broadcast sent to the room. If I replace socket.to(roomName).emit('join', currentUser); with socket.emit('join', currentUser); the client receives the broadcast, but I'd like to use rooms here. Any help would be greatly appreciated.
app.js
// Game
app.get('/game', (req, res) => {
const cookies = req.cookies;
const currentUser = cookies['current_user'];
const roomName = cookies['room_name'];
if (roomName) {
res.render('pages/game', {
room: roomName
});
io.of('/game').on('connection', socket => {
console.log('a user connected');
socket.on('disconnect', () => {
console.log('user disconnected');
});
socket.join(roomName);
socket.to(roomName).emit('join', currentUser);
});
} else {
res.redirect('/login');
}
});
game.ejs
const socket = io("/game");
socket.on('join', function(user) {
console.log(`${user} joined`);
});
Also, could I replace this:
if (roomName) {
// stuff
} else {
// other stuff
}
with this:
if (!roomName) {
// other stuff
return;
}
// stuff

According to the documentation, if you use socket.to('room').emit(...) instead of io.in('room').emit(...), it will be broadcasted to all sockets in the room except the sender. That's where your problem lies.
The reason socket.emit(...) works is because you're sending it directly and only to the sender.
This emit cheatsheet is quite useful to figure out which combinations of io/socket and to etc affect where messages get sent to.

Related

How socket.io can be used like axios?

I've an application, which is built in axios, just PUT, POST, DELETE, GET in mind. which looks like this
getAPI = axios.create(.....)
....
getAPI.post('signup/', {email, password})
.then(res => {
/// return some res
})
.catch(err => {
/// some error is show if not succeed
})
}
and also goes or "post/", "logout/", "signup/" with different methods.
Later i found that, In order to make post actions realtime in client side , we need to use websocket. So i used socket.io .
I've already setup server and client.
In server socket connection like this
io.on('connection', socket => {
console.log('User is connected on socket');
socket.on('disconnect', () => console.log('disconnected'));
})
and in client connection i've searc tutorials and used contextAPI, and passed to allcomponents.
in my specific component, where i've user post and user post is shown i've put code like this
const {socket} = useContext(AuthContext);
useEffect(() => {
socket.on("connect", () => {
console.log("client connected")
})
return ()=> socket.disconnect()
})
Now how can i use those of axios request with catch errors but with socket.io. It seems very hard to me using socket.io integrating with axios . Although i need not to use socket on authentication. But i need to use it on "/post" request.
Posting from client to server was easy by that axios.POST.then().catch(), axios.GET .....
but i'm confused to integrate that axios things in socket in client .
Also in backend side , i've routes like these
router.get('/logout', logout)
router.post('/post/create', post)
with each handler like these
exports.postCreate = (req, res) => {
let post = new Post(req.body)
post.save((err, post) => {
if(err){
return res.status(400).json({error: "Error"})
}
return res.json(post)
})
}
but if i want to use socket.io, what should i do? I'm very confused by socket.io docs, not showing for handeling things.
If you have idea about these things,
please answer me Thank you for your answer
Socket.io keeps its connections alive. In order to handle errors. You will need to listen to events. For example:
Handling connection errors:
socket.on("connect_error", (error) => {
// ...
});
Handling disconnect errors:
socket.on("disconnect", (reason) => {
if (reason === "io server disconnect") {
// the disconnection was initiated by the server, you need to reconnect manually
socket.connect();
}
// else the socket will automatically try to reconnect
});
If you'd like to ensure that your server side handled your request and need confirmation you can use the optional 'ack' feature like this:
// client side
socket.emit("ferret", "tobi", (data) => {
console.log(data); // data will be "woot"
});
// server side:
io.on("connection", (socket) => {
socket.on("ferret", (name, fn) => {
fn("woot");
});
});

Implement unread messages

I am implementing a live chat.
Here is my server file.
io.on('connection', socket => {
socket.on('userConnect', async (room, user_id) => {
socket.join(room);
});
socket.on('send-chat-message', (room, sender_id, userMessage) => {
const message = new Message(room, sender_id, userMessage);
db.query(message.saveMessage());
socket.to(room).emit('chat-message', { message: userMessage, user: sender_id });
});
});
This is how I implemented the connection to the room and the connection for sending messages from the client.
How can I make a function that the user has not yet read the message?

Connections are duplicationg in Socket.io

I'm developing a chat app, and the connections are duplicating.
In my route, I have:
exports.index = function (io) {
return function (req, res) {
var userId;
res.render('index', {
title: 'RandomChat.me',
test: 'test2'
});
io.sockets.on('connection', function (socket) {
userId = socket.id;
console.log("+++++++++++++++++++" + userId);
socket.emit('addUser', { userId: userId });
socket.room = 'General';
socket.join(socket.room);
socket.on('sendMessage', function (data) {
console.log(data.room);
// socket.broadcast.emit('receiveMessage', { data: data });
socket.broadcast.to(data.room).emit('receiveMessage', { message: data.message });
});
});
}
};
Client-side something like:
var socket = io.connect('http://domain.com:3000');
var userId;
socket.on('addUser', function(data) {
userId = data.userId;
console.log(userId);
});
socket.on('receiveMessage', function (data) {
console.log(data);
});
var room: "General";
var message: "Test";
socket.emit('sendMessage', { room : room, message: message });
console.log(userId + " " + message)
If I go to the app and check the console log, I see the userId, and when I reload the page, I see the same ID's twice, if I reload again, I see it 3 times and so on.
The same thing is happening in the node.js console.
So basically when connections/users are duplicated, the other users receive duplicate messages, as the sendMessage/receiveMessages functions are run more than once.
Help appreciated.
The problem is this line
io.sockets.on('connection', function (socket) {
You should not put these inside a request handler because its just wrong to add a connection handler for socket for each request. Try doing these outside the request handler and use the io object to emit/broadcast events to sockets from the request handler.
Using io.sockets.once('connection', function (data){}) instead of io.sockets.on('connection', function (data){}) fixed it. It doesn't try to recover a lost connection.

How to emit an event in socket.io from the routes file?

This is my app configuration
app.js
//SERVER
var server = app.listen(3000, function(){
console.log("Express server listening on port %d in %s mode", app.get('port'),
app.settings.env);
});
//SOCKET.IO
var io = require('./socket.io').listen(server)
/socketio
var socketio = require('socket.io')
module.exports.listen = function(app)
{
io = socketio.listen(app);
io.configure('development',function()
{
//io.set('transports', ['websocket', 'xhr-polling']);
//io.enable('log');
});
io.configure('production',function()
{
io.enable('browser client minification'); // send minified client
io.enable('browser client etag'); // apply etag caching logic based on version number
io.set('log level', 1); // reduce logging
io.set('transports', [ // enable all transports (optional if you want flashsocket)
'websocket'
, 'flashsocket'
, 'htmlfile'
, 'xhr-polling'
, 'jsonp-polling'
]);
});
io.sockets.on('connection', function (socket)
{
console.log("new connection: "+socket.id);
socket.on('disconnect',function(){console.log("device "+socket.id+" disconnected");});
socket.emit('news', { hello: 'world' });
socket.on('reloadAccounts',function(data)
{
var accounts=['account1','account2']
socket.emit('news',accounts)
});
});
return io
}
/routes
exports.newAccount=function(fields,callback)//localhost:3000/newAccountForm
{
//... bla bla bla config db connection bla bla bla
db.collection('accounts').insert(fields,function(err,result)
{
if(err)
{
console.warn(err);
db.close();
return callback(err,null);
}else{
if(result)
{
db.close();
return callback(null,result);
socket.emit('new account created',result) // i want to emit a new event when any user create an account
}else{
db.close();
return callback('no se consigue resultado',null);
}
}
})
});
}
How to emit an event in socket.io from the routes file?
First you need to decide that what socket you want to send the new info. If it's all of them(to everyone connected to your app), it would be easy, just use io.sockets.emit:
In the ./socket.io file you add exports.sockets = io.sockets; somewhere after io = socketio.listen(app);. Then in the routes file, you can emit like this:
var socketio = require('./socket.io');
socketio.sockets.emit('new account created', result);
If you know the socket id that you want to send to, then you can do this:
var socketio = require('./socket.io');
socketio.sockets.sockets[socketId].emit('new account created', result);
You can also select the socket by express session id:
First you need to attach the session id to the socket on authorization:
io.set('authorization', function (data, accept) {
// check if there's a cookie header
if (data.headers.cookie) {
// if there is, parse the cookie
data.cookie = cookie.parse(data.headers.cookie);
// note that you will need to use the same key to grad the
// session id, as you specified in the Express setup.
data.sessionID = data.cookie['express.sid'];
} else {
// if there isn't, turn down the connection with a message
// and leave the function.
return accept('No cookie transmitted.', false);
}
// accept the incoming connection
accept(null, true);
});
Then you can select sockets with the session id:
var socketio = require('./socket.io');
var sockets = socketio.sockets.forEach(function (socket) {
if (socket.handshake.sessionID === req.sesssionID)
socket.emit('new account created', result);
});
You can also query your session store and using the method I described above, emit the event to sockets with sessionId that matched your query.

How to know when channel is subscribed/unsubscribed with socket.io

Just searched all the web to find how can I track inside node.js server when a channel is subscribed or unsubscribed. What I can do right know is the connect and disconnect bindings, but no clue how to do it with channels.
io.sockets.on('connection', function (socket) {
console.log("["+socket.id+"] Connected");
// handler to know when a socket subscribed a channel (*) ?
// handler to know when a socket unsubscribed a channel (*) ?
socket.on('disconnect', function () {
console.log("["+socket.id+"] Disconnect");
});
});
Is it possible?
You are looking for "socket.of('channelName')" ...
See Socket.io docs
SERVER:
var io = require('socket.io').listen(80);
var chat = io
.of('/chat')
.on('connection', function (socket) {
socket.emit('a message', {
that: 'only'
, '/chat': 'will get'
});
chat.emit('a message', {
everyone: 'in'
, '/chat': 'will get'
});
});
var news = io
.of('/news')
.on('connection', function (socket) {
socket.emit('item', { news: 'item' });
});
CLIENT:
<script>
var chat = io.connect('http://localhost/chat')
, news = io.connect('http://localhost/news');
chat.on('connect', function () {
chat.emit('hi!');
});
news.on('news', function () {
news.emit('woot');
});
</script>

Categories