I am using socket io with a simple token auth method (server side):
io.use(function(socket, next){
var handshake = socket.request;
if(token == handshake._query.token)
{
console.log('Client authorized');
next();
}
else
{
console.log('Client not authorized');
next(new Error('not_authorized'));
socket.disconnect();
}
});
The problem is that if the client failed the first time authorization (wrong token), it does not retry to connect any more, even If I send a manual new connection it will not recconect until a total page refresh.
Cliend side:
var socket = io.connect(this.adress, {query: 'token=123456789', transports:['websocket', 'polling'] });
socket.on('error', function(err){
console.log(err);
if(err == 'not_authorized')
{
console.log('gettin toke');//NOT ENTERING HERE
}
PS.connected = false;
});
How can I retry reconnect with another parameter?
Workaround, set this variables againg to retry connecting:
PS.socket.socket.connecting = false;
PS.socket.socket.options.query = 'token='+tok;
According to the documentation sample you should not use socket.disconnect();. Your client-side error event does not fire exactly because of this line.
Edit:
You may also try to set forceNew to true in connection options when reconnecting:
var socket = io.connect(this.adress, {
query: 'token=123456789',
transports:['websocket', 'polling'],
forceNew: true
});
Related
I'm new to node
I have a PHP/Laravel cms and I have a simple Nodejs game server which is basically a loop generating some numbers
I connect my PHP backend to Nodejs via Socketio and use Socketio-JWT to identify the user
my client side (php/laravel)
PHP
$userToken = JWTAuth::customClaims(['userid' => Auth::user()->id, 'name' => Auth::user()->name, 'avatar' => asset_url('image/avatar-default.png')])->fromUser(Auth::user() );
html/js
var socket = io.connect("http://localhost:666");
socket.on('connect', function () {
socket.emit('authenticate', {token: '{{$userToken}}'});
});
socket.on('authenticated', function () {
console.log('Authenticated');
});
socket.on('unauthorized', function (data) {
console.log('Unauthorized, error msg: ' + data.message);
});
my serverside
const _online_users = { };
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
io.sockets.emit('update_online_users' , _online_users );
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
}) ;
as you can see I have an object called _online_users and I store authenticated users in this object and then I send it to the clients so they know who is online
io.sockets.emit('update_online_users' , _online_users );
here is the problem, when the user gets disconnected
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`)
});
I have to update my _online_users object and remove disconnected user .... how should I go about this? I was thinking maybe I can store the token itself in the _online_users
_online_users[socket.decoded_token.userid] = {
token : socket.token ,
name : socket.decoded_token.name ,
avatar : socket.decoded_token.avatar ,
}
and when the user gets disconnected I get the disconnected token from the socket and remove the user from an object by that token
of course, this is all theory! I'm not sure if that's the way to go .... first of all, I can't access the token itself from the socket !
or let's say one of the users sends another request to node server, how can I identify the user sending the request?
.on('authenticated', function(socket) {
socket.on('somaction', function() {
console.log(` who is this guy ? `)
});
})
is there anything unique insocket.decoded_token that I can use as id? if so I can store it in the online users send it back and forth when the user requests something
basically I'm lost and appreciate any pointers
You can use a middleware now in the newer versions of socket-io. So you can check if the user is logged in by the jwt token that is sent with the request. If decoded successfully you can assign the user info to the current socket and call next() and go to the event you are listening for. Here is the example provided in the socket-io docs slightly modified for your case.
io.use(function(socket, next) {
const handshakeData = socket.request;
// make sure the handshake data looks good as before
// if error do this:
// next(new Error('not authorized'));
// else decode jwt token here and append the user to the socket.request
// and call next
// pseudo code here
const {
authorization
} = handshakeData.header
let token;
if (authorization && authorization.split(" ")[0] === "Bearer") {
token = authorization.split(" ")[1]
}
let user = jwt.decode(token, secret);
socket.request.user = user;
next();
});
You are doing well!
Since you are adding socket event handlers in the 'authenticated' handler you still have access to socket.decoded_token.userid.
This should be enough:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
_online_users[socket.decoded_token.userid] = {
name: socket.decoded_token.name,
avatar: socket.decoded_token.avatar,
};
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
console.log(`----- ##disconnect -----`);
delete _online_users[socket.decoded_token.userid];
io.sockets.emit('update_online_users', _online_users);
});
});
or to be a little more concise:
const _online_users = {};
io.sockets
.on('connection', socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
}))
.on('authenticated', function(socket) {
const { avatar, name, userid } = socket.decoded_token;
_online_users[userid] = { name, avatar };
io.sockets.emit('update_online_users', _online_users);
socket.on('disconnect', function() {
delete _online_users[userid];
io.sockets.emit('update_online_users', _online_users);
});
socket.on('any other event...', function() {
// ... still have access to userid
});
});
Edit: About unauthenticated socket I don't know; doc says nothing. You could try something like:
io.socket.on('connection', socket => {
socket.emit('update_online_users', _online_users);
// I'm afraid this closes the socket if unauthorized, you could check by yourself
socketioJwt.authorize({
secret: JWT_SECRET,
timeout: 15000
})(socket);
}).on('authenticated', socket => {
//...
});
Hope this helps.
I have a Node/Vue application. I am consuming a WebSocket from Binance, a crypto exchange. I can see the quotes on the server console as I log them, I can send them to the browser for a short period of time before the client stops logging them.
Browser just using WebSocket API
Node using ws library
Node code, this I am running as it's own service as its just this.
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.on('connection', function connection(ws){
console.log("Connected a new client");
ws.send(data);
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
})
On the Client side I am using Vue and in the app.js file I have this on the created hook.
let socket = new WebSocket("ws://localhost:5002")
socket.addEventListener('message', function(event){
let quotes = JSON.parse(event.data);
console.log(quotes.p)
});
socket.addEventListener('error', function(event){
console.log("closing because " + event);
})
Right now I am only listening to the consoles in the above app.vue file.
What I see in the browser console is a lot of quotes, then they stop after a second or 2. There can be over a thousand quotes in some times. Then on occasion I see a console.log('created') that I have in a child component of app.vue. In many cases this is the last thing in the console after hundreds of quotes.
In the console.log for the server I see a lot of sessions being created with one page refresh. So much that it fills my console.
So I'm not sure I am creating the connections correcly, I am not sure if Vue is somehow stopping the console.log's?
I don't see any errors anywhere and the entire time in my server console the Binance API continues streaming.
you have to write server event listener outside binance on message handler;
then you can pass messages from binance to the server by emitting new event to the server
on receiving message from binance you can send data to all connection on the server
Or Try this code I think it will work :
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
server.on('connection', function connection(ws){
console.log("Connected a new client");
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
})
Yes, I have gone through the documentation, which is very well written:
Socket IO Cheatsheet
Here is the problem: I want to notify the user of a logout when his session from the Express App is being destroyed. Now this is what is happening: When I log out from the session, all other clients (including those who have or have not even logged in) get a message saying they're logged out. Yes, my express app is working fine - they are not getting logged off, but I believe SOCKET IO is sending them the message regardless. I ran the debugger and it turns out that both the clients are distinguishable, too.
Here is my code:
server.js:
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.set('socketio', io);
io.on('connection', function (socket) {
app.set('current_socket', socket);
console.log('No of clients:', io.engine.clientsCount);
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const io = req.app.get('socketio');
const this_socket = req.app.get('current_socket');
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
I even tried this instead:
io.emit('userAction', { action: 'logout' });
but turns out it still emits to all the clients. I am pretty sure there is a mismatch somewhere, just can't figure out where.
You need create room for each session id if you want to send emits to spesific user
io.on('connection', function (socket) {
app.set('current_socket', socket);
var sessionId = socker.request.session.id
//join room
socket.join(sessionId);
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const io = req.app.get('socketio');
const this_socket = req.app.get('current_socket');
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.sockets.in(sessionID).emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
You have to define unique socket object for each user. We have many ways to do that.
In simple way, we use user id (unique) as a key to store socket object (Map way: key(userId) - vaule(socketObj)).
Follow the rabbit:
When a user loggedin, client side emits a event (login) to server side, the event include the user id.
Client Side:
// login success
socket.emit('userLoggedIn', {userId: THE_USER_ID})
Server Side:
io.on('connection', function (socket) {
// app.set('current_socket', socket);
console.log('No of clients:', io.engine.clientsCount);
socket.on('userLoggedIn', function(data) => {
app.set(data.userId, socket); // save socket object
})
});
userController.js:
exports.userLogout = function(req, res, next) {
const sessionID = req.session.id;
const userId = MAGIC_GET_USER_ID_FROM_SESSION_ID(sessionID) // who want to logout
const io = req.app.get('socketio');
const this_socket = req.app.get(userId); // get "user socket"
req.session.destroy(function (err){
if(err) {
console.error("userLogout failed with error:", err);
return next(err);
}
else {
console.log("this_socket:", this_socket);
console.log("io:", io);
this_socket.emit('userAction', { action: 'logout' });
//other logic to remove old sessions from DB Session Store
//and finally:
res.status(200)
.json({
status: 'success',
api_data: {'loggedIn':false},
message: 'Logout successful.'
});
}
}
}
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var fs = require('fs');
var mysql = require('mysql');
app.get('/', function(req, res){
res.send('<h1>My App</h1>');
});
var db_config = {
host: 'localhost',
user: 'root',
database: 'database',
password: '',
dialect: 'mysql',
insecureAuth: true
};
var connection;
function handleDisconnect() {
connection = mysql.createConnection(db_config); // Recreate the connection, since
// the old one cannot be reused.
connection.connect(function(err) { // The server is either down
if(err) { // or restarting (takes a while sometimes).
console.log('error when connecting to db:', err);
setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
} // to avoid a hot loop, and to allow our node script to
}); // process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
connection.on('error', function(err) {
console.log('db error', err);
if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
handleDisconnect(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
throw err; // server variable configures this)
}
});
}
handleDisconnect();
http.listen(3000, function(){
console.log('listening on *:3000');
});
.............
I am developing an app that returns real time score updates from database as shown in the code above. When I execute the program above with localhost (wamp) it works fine. When executing with CentOS 7 with MariaDB it returns an error stating:
db error { Error: Connection lost: The server closed the connection.
I have done some changes to my code by following this thread.
But it didn't work. And also I tried with Pooling also. Help me to resolve this issue.
Working with Node.js(monogdb, express and other modules)
I'm wondering if there is a mongoose method for database connection, something like if I open a connection var db = mongoose.connect('mongo://localhost/members'); then I can db.on('close', function(){ /*do stuffs here*/}).
Basicly, the function below does the job of getting a user list from database and logging when database connection is closed.
So I need something in the if() to check database connection or whatever just unable to get data while its off and make a logging. I tried if(docs != null) it seems just off tho. Any advice would be much appreciated!
var logger = require('bunyan');
var log = new logger({
name: "loggings",
streams: [
{
level: 'error',
path: 'test.log',
}
],
serializers: {
err: logger.stdSerializers.err,
}
});
function(req, res){
memberModel.find(function(err, docs){
if (/*connection is closed*/) {
res.render('users.jade', { members: docs });
}else{
try {
throw new DatabaseError ("Error!");
} catch (err){
log.warn({err: err}, "Check database connection!");
}
res.render('index.jade');
};
});
};
Why are you checking in the place you are for the database connection being closed? If it is closed at that point, then it is likely that docs will be null. I think checking
if (!err) {
res.render('users.jade', { members : docs });
} else {
/* throw err, etc */
is a reasonable course to take.
Since you want to log when the database connection is closed, why not attach a logging function to the Db's "close" event?
If I've misunderstood and checking for the connection's state is what you really want, then I'd recommend playing around with the properties of:
http://mongoosejs.com/docs/api.html#connection_Connection
(hit the "show code" button). The connection's _closeCalled property looks like it may be what you want to check.
You can use mongoose events that will get fire whenever your server get disconnected:
mongoose.connection.on('error', function (err) {
console.log('Mongoose default connection error: ' + err);
//HERE SERVE YOUR DISCONNECT WARNING PAGE AND LOGGER
});
mongoose.connection.on('disconnected', function () {
console.log('Mongoose default connection disconnected');
//HERE SERVE YOUR DISCONNECT WARNING PAGE AND LOGGER
});
var uri = 'mongodb://localhost/user1';
var promise = mongooose.connect(uri,{
useMongoClient: true,
});
promise.openUri(uri,function(errr,db){
if(errr){
throw errr;
}else{
console.log("Connection Successfull");
glo_db = db;
}
});
Above could needs to be written for the newer version of mongoose and would throw error if any error is found while connecting to a database.
Check here