How to make sure a callback function has called(fired)?
I am using socket.io and callback function please check out my code:
// server
socket.emit('message', message, function() {
// some code
});
// client
socket.on('message', function(data, callback) {
callback(); // confirm we received the message
// some code
});
I want to know in the server code, to detect whether function has called in the client side or no.
If I understood you well, to detect in your server whether your callback has been called in the client or not, you can use a timer in the server and emit a confirmation in the client.
Let's me explain it further.
1) Server
// Timer to wait for your confirmation
let timer
// Listen message from the Client
socket.on('message', msg => {
// Wait 5 s for confirmation
timer = setTimeout(() => noConfirmation(), 5000)
// Send message to the Clients
io.emit('message', msg)
})
2) Client
// Listen message from the Server
socket.on('message', msg => {
// Send confirmation (your callback)
socket.emit('confirmation', id)
})
3) Server
// Listen confirmation from the Client
socket.on('confirmation', id => {
// Avoid timer
clearTimeout(timer)
// Send back confirmation
io.emit('confirmation', id)
})
Here you are a full working example:
Server (index.js)
const app = require('express')()
const http = require('http').createServer(app)
const io = require('socket.io')(http)
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html')
})
// Timer to wait for your confirmation
let timer
io.on('connection', socket => {
console.log('a user connected')
socket.on('disconnect', () =>
console.log('user disconnected'))
// Listen message from the Client
socket.on('message', msg => {
console.log(`message: ${msg}`)
// Wait 5 s for confirmation
timer = setTimeout(() => console.log('☓'), 5000)
// Send message to the Clients
io.emit('message', msg)
})
socket.on('confirmation', id => {
console.log('✓')
// Avoid timer
clearTimeout(timer)
// Send back confirmation
io.emit('confirmation', id)
})
})
http.listen(3000, () =>
console.log('listening on *:3000'))
Client (index.html)
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io()
// Submit message to the Server
const $input = document.querySelector('input');
document.querySelector('form').onsubmit = e => {
e.preventDefault() // prevents page reloading
socket.emit('message', $input.value)
$input.value = ''
}
// Listen message from the Server
const $messages = document.querySelector('#messages');
socket.on('message', msg => {
const id = new Date().getTime()
$messages.insertAdjacentHTML('beforeend', `<li id="m${id}">${msg}</li>`)
// Send confirmation
socket.emit('confirmation', id)
})
// Confirmation message recived
socket.on('confirmation', id =>
document.querySelector(`#m${id}`).insertAdjacentText('beforeend', '✓'))
</script>
</body>
If you don't receive the check symbol (✓) it means there was a problem with the messages and in the server after 5 seconds we show a cross symbol (☓), also you can use any function you want in both cases. We are covering both directions.
Hope this help or at least point you to the right direction : )
You can transform your function to make it async (See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
It will look like this:
socket.on('message', async function(data, callback) {
const result = await callback(); // confirm we received the message
console.log(result); //returns whatever the callback function returned
});
Related
I'm trying to build a simple chatroom application using Flask and socket-io. When a user types a message and clicks on the submit button, the message should be broadcasted to all users. But in my case, after clicking the submit button the page refreshes and nothing appears. Please help.
I have tried running the Javascript code in the firefox browser console and it works fine there(without forms). But when I submit the form from webpage the problem arises.
here's is some code snippet:
python backend:
#socketio.on('send messages')
def vote(data):
messages = data['messages']
emit('announce messages', {"messages":messages}, broadcast=True)
javascript:
document.addEventListener('DOMContentLoaded', () => {
// Connect to websocket
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
// When connected, configure buttons
socket.on('connect', () => {
document.querySelector('#form').onsubmit = () => {
const messages = document.querySelector('#messages').value;
socket.emit('send messages', {'messages': messages});
}
});
socket.on('announce messages', data => {
const li = document.createElement('li');
li.innerHTML = `${data.messages}`;
document.querySelector('#push').append(li);
});
});
HTML form
<ul id="push">
<!--filled by socket.io-->
</ul>
<form id="form">
<label for="messages">Message:</label>
<input id="messages" type="text" name="messages">
<input type="submit" value="Submit">
</form>
When you submit your form, you have to prevent it from actually submitting by returning False so that the connection doesn't drop out. In your case, page is getting refreshed and therefore you are not able to see the result.
socket.on('connect', () => {
document.querySelector('#form').onsubmit = () => {
const messages = document.querySelector('#messages').value;
socket.emit('send messages', {'messages': messages});
return False
}
});
Maybe this will do the work.
I am working with socket.io example for chat project. I want to add password protection to the chat application. This is just simple authentication. If user input wrong password, then server will disconnect this user.
Client side code:
var socket = io();
socket.emit('user name', username);
socket.emit('password', password);
Server side code:
socket.on('password', function (msg) {
if (msg !== '123321') {
io.emit('system info', 'Wrong password... Disconnecting ' + username+'...');
io.close(true);
return false;
}
});
The close() method doesn't work. the disconnected user still able to receive message from other users. So how to do this properly? Thank you.
Assuming you are creating an id (server side) with something like:
io.engine.generateId = (req) => {
return custom_id++; // custom id must be unique
}
You could close an specific connection using .disconnect() and you could call the id with socket.id
socket.on('password', function (msg) {
if (msg !== '123321') {
io.emit('system info', 'Wrong password... Disconnecting ' + username+'...');
io.sockets.connected[socket.id].disconnect();//<-- in this case it's just a number
}
});
I'm using rooms to send a 'private' message to a client and it works however the message being sent is duplicated by the number of clients I have and I believe this is because of my .on('message') since this is still triggering for every client but only emitting to the correct client (but multiple times..)
server
io.sockets.on('connection', function(socket {
socket.on('join', function(data)
{
console.log('enter');
socket.join(data.user);
});
var rClient = redis.createClient();
rClient.subscribe('messagex');
rClient.on('message', function(channel, message) {
io.in(message.user).emit('messagex', {content: message.content});
});
socket.on('disconnect', function() {
rClient.quit();
});
});
receiver client
<script>
var username = prompt("test");
var socket = io.connect('http://localhost:8100');
socket.emit('join', {user: username});
socket.on('messagex', function(data) {
$('#messages').append('<p>' + data.content + '</p>');
});
So I have 3 clients (each with different users/rooms open) at the receiver page and I send a message from the sender to say user user1, then I will only receive the message on user1 client but it will receive 3 of the same message each time and the number of times duplicated seems to be the number of clients I have..
try this
subscribe.unsubscribe(channel);
when connection disconnect unsubscribe
io.sockets.on('connection', function(socket {
socket.on('join', function(data)
{
console.log('enter');
socket.join(data.user);
});
var rClient = redis.createClient();
rClient.subscribe('messagex');
rClient.on('message', function(channel, message) {
io.in(message.user).emit('messagex', {content: message.content});
});
socket.on('disconnect', function() {
rClient.unsubscribe('messagex');
rClient.quit();
});
});
I think you are using two channel at same time redis and socket.io, You have to make single channel i.e. socket.io only then there is no need make redis channel means no need to add pub/sub method when you transferring data through emit/on.
I have been following a tutorial and a github (https://github.com/mmukhin/psitsmike_example_2) on how to make a multi room simple chat app. I'm fairly new to this.
I am trying to modify it to list the usernames in the right hand side under the room list. But it's not working, it doesn't seem to be throwing any errors though?
I have done the following:
index.html
I have added the following code to the original:
<div id="roomusers"></div>
and
socket.on('updateroomusers', function(roomusers) {
$("#roomusers").empty();
$.each(roomusers, function (key, value) {
$('#roomusers').append('’+value+”);
});
});
This is the original
<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');
// on connection to server, ask for user's name with an anonymous callback
socket.on('connect', function(){
// call the server-side function 'adduser' and send one parameter (value of prompt)
socket.emit('adduser', prompt("What's your name?"));
});
// listener, whenever the server emits 'updatechat', this updates the chat body
socket.on('updatechat', function (username, data) {
$('#conversation').append('<b>'+username + '> </b> ' + data + '<br>');
});
// listener, whenever the server emits 'updaterooms', this updates the room the client is in
socket.on('updaterooms', function(rooms, current_room) {
$('#rooms').empty();
$.each(rooms, function(key, value) {
if(value == current_room){
$('#rooms').append('<div>' + value + '</div>');
}
else {
$('#rooms').append('<div>' + value + '</div>');
}
});
});
function switchRoom(room){
socket.emit('switchRoom', room);
}
// on load of page
$(function(){
// when the client clicks SEND
$('#datasend').click( function() {
var message = $('#data').val();
$('#data').val('');
// tell server to execute 'sendchat' and send along one parameter
socket.emit('sendchat', message);
});
// when the client hits ENTER on their keyboard
$('#data').keypress(function(e) {
if(e.which == 13) {
$(this).blur();
$('#datasend').focus().click();
}
});
});
socket.on('updateroomusers', function(roomusers) {
$("#roomusers").empty();
$.each(roomusers, function (key, value) {
$('#roomusers').append('’+value+”);
});
});
</script>
<div style="float:left;width:100px;border-right:1px solid black;height:300px;padding:10px;overflow:scroll-y;">
<b>ROOMS</b>
<div id="rooms"></div>
<b>USERS</b>
<div id="roomusers"></div>
</div>
<div style="float:left;width:300px;height:250px;overflow:scroll-y;padding:10px;">
<div id="conversation"></div>
<input id="data" style="width:200px;" />
<input type="button" id="datasend" value="send" />
</div>
And the app.js (everything between the comments
/////////**** Userlist Start ***********///////
and
/////////**** Userlist End***********/////// is code I added
var express = require('express')
, app = express()
, http = require('http')
, server = http.createServer(app)
, io = require('socket.io').listen(server);
server.listen(8080);
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// usernames which are currently connected to the chat
var usernames = {};
// rooms which are currently available in chat
var rooms = ['room1','room2','room3'];
io.sockets.on('connection', function (socket) {
// when the client emits 'adduser', this listens and executes
socket.on('adduser', function(username){
// store the username in the socket session for this client
socket.username = username;
// store the room name in the socket session for this client
socket.room = 'room1';
// add the client's username to the global list
usernames[username] = username;
// send client to room 1
socket.join('room1');
// echo to client they've connected
socket.emit('updatechat', 'SERVER', 'you have connected to room1');
// echo to room 1 that a person has connected to their room
socket.broadcast.to('room1').emit('updatechat', 'SERVER', username + ' has connected to this room');
socket.emit('updaterooms', rooms, 'room1');
/////////**** Userlist Start ***********///////
// get all the clients in ‘room1′
var clients = io.sockets.clients('room1');
// loop through clients in ‘room1′ and add their usernames to the roomusers array
for(var i = 0; i < clients.length; i++) {
roomusers[roomusers.length] = clients[i].username;
/////////**** Userlist End ***********///////
}
// broadcast to everyone in room 1 the usernames of the clients connected.
io.sockets.to('room1').emit('updateroomusers',roomusers);
});
// when the client emits 'sendchat', this listens and executes
socket.on('sendchat', function (data) {
// we tell the client to execute 'updatechat' with 2 parameters
io.sockets.in(socket.room).emit('updatechat', socket.username, data);
});
socket.on('switchRoom', function(newroom){
socket.leave(socket.room);
socket.join(newroom);
socket.emit('updatechat', 'SERVER', 'you have connected to '+ newroom);
// sent message to OLD room
socket.broadcast.to(socket.room).emit('updatechat', 'SERVER', socket.username+' has left this room');
// update socket session room title
socket.room = newroom;
socket.broadcast.to(newroom).emit('updatechat', 'SERVER', socket.username+' has joined this room');
socket.emit('updaterooms', rooms, newroom);
});
// when the user disconnects.. perform this
socket.on('disconnect', function(){
// remove the username from global usernames list
delete usernames[socket.username];
// update list of users in chat, client-side
io.sockets.emit('updateusers', usernames);
// echo globally that this client has left
socket.broadcast.emit('updatechat', 'SERVER', socket.username + ' has disconnected');
socket.leave(socket.room);
});
});
It was really easy setting up sessions and using them in PHP. But my website needs to deal with WebSockets. I am facing problem to set up sessions in node.js. I can easily push data without using sessions and it would work fine but when more than one tab is opened the new socket.id is created and previously opened tabs won't function properly. So I have been working on sessions and had problem accessing session store, its logging session not grabbed. I have tried with session.load as well but no luck
How do I get session object and use it in a way that opening other tabs wouldn't affect the functionality and push data from server to client on all tabs?
var express=require('express');
var http = require('http');
var io = require('socket.io');
var cookie = require("cookie");
var connect = require("connect"),
MemoryStore = express.session.MemoryStore,
sessionStore = new MemoryStore();
var app = express();
app.configure(function () {
app.use(express.cookieParser());
app.use(express.session({store: sessionStore
, secret: 'secret'
, key: 'express.sid'}));
app.use(function (req, res) {
res.end('<h2>Hello, your session id is ' + req.sessionID + '</h2>');
});
});
server = http.createServer(app);
server.listen(3000);
sio = io.listen(server);
var Session = require('connect').middleware.session.Session;
sio.set('authorization', function (data, accept) {
// check if there's a cookie header
if (data.headers.cookie) {
// if there is, parse the cookie
data.cookie = connect.utils.parseSignedCookies(cookie.parse(data.headers.cookie),'secret');
// 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'];
sessionStore.get(data.sessionID, function (err, session) {
if (err || !session) {
// if we cannot grab a session, turn down the connection
console.log("session not grabbed");
accept('Error', false);
} else {
// save the session data and accept the connection
console.log("session grabbed");
data.session = session;
accept(null, true);
}
});
} 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);
});
sio.sockets.on('connection', function (socket) {
console.log('A socket with sessionID ' + socket.handshake.sessionID
+ ' connected!');
});
Take a look at this article: Session-based Authorization with Socket.IO
Your code works fine, but need 2 improvements to do what you want (send session data to clients from server):
it extracts sessionID during authorization only
it extracts session data from store by this sessionID during connection where you can send data from server to clients in an interval.
Here's the improved code:
var express = require('express');
var connect = require('connect');
var cookie = require('cookie');
var sessionStore = new express.session.MemoryStore();
var app = express();
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.session({store: sessionStore, secret: "secret", key: 'express.sid'}));
// web page
app.use(express.static('public'));
app.get('/', function(req, res) {
var body = '';
if (req.session.views) {
++req.session.views;
} else {
req.session.views = 1;
body += '<p>First time visiting? view this page in several browsers :)</p>';
}
res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');
});
var sio = require('socket.io').listen(app.listen(3000));
sio.set('authorization', function (data, accept) {
// check if there's a cookie header
if (data.headers.cookie) {
// if there is, parse the cookie
var rawCookies = cookie.parse(data.headers.cookie);
data.sessionID = connect.utils.parseSignedCookie(rawCookies['express.sid'],'secret');
// it checks if the session id is unsigned successfully
if (data.sessionID == rawCookies['express.sid']) {
accept('cookie is invalid', false);
}
} 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);
});
sio.sockets.on('connection', function (socket) {
//console.log(socket);
console.log('A socket with sessionID ' + socket.handshake.sessionID + ' connected!');
// it sets data every 5 seconds
var handle = setInterval(function() {
sessionStore.get(socket.handshake.sessionID, function (err, data) {
if (err || !data) {
console.log('no session data yet');
} else {
socket.emit('views', data);
}
});
}, 5000);
socket.on('disconnect', function() {
clearInterval(handle);
});
});
Then you can have a client page under public/client.html at http://localhost:3000/client.html to see the session data populated from http://localhost:3000:
<html>
<head>
<script src="/socket.io/socket.io.js" type="text/javascript"></script>
<script type="text/javascript">
tick = io.connect('http://localhost:3000/');
tick.on('data', function (data) {
console.log(data);
});
tick.on('views', function (data) {
document.getElementById('views').innerText = data.views;
});
tick.on('error', function (reason){
console.error('Unable to connect Socket.IO', reason);
});
tick.on('connect', function (){
console.info('successfully established a working and authorized connection');
});
</script>
</head>
<body>
Open the browser console to see tick-tocks!
<p>This session is viewed <b><span id="views"></span></b> times.</p>
</body>