Socket.io multiple connections when refreshing pag

2019-08-18 07:20发布

问题:

I'm working in a real-time app, and I'm using Socket.io to get this feature.

What I have so far is, when a user post something (text), my sockets server receive this message and then it sends to my controller to save this data on a DB (MySQL). Then the same message is returned to the client through the same socket server.

Actually this works well, but when I refresh my browser and post a new message, the data is inserted twice in the database, so I receive the same two messages in my client. This happens everytime that I refresh the page, so if I've refreshed the page 10 times, I will insert 10 messages with the same data on my DB.

This is the code I have right now, any help would be great.

SocketServer.js

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

//websockets
var messages = 'Recibido';
var Usuarios = 0;
var storeUsers = [];

app.use(express.static('public'));

io.sockets.on('connection', function(socket) {
  Usuarios++;

  console.log('Usuarios totales: ' + Usuarios);
  io.sockets.emit('users-connected', Usuarios);

  var clients = io.sockets.clients();
  var conectados = clients.connected;

  console.log('------------------ROOM-------------------------');
  for (client in conectados) {
    var color = '\x1b[32m' + client;
    console.log('id: ' + color, '\x1b[0m');
  }
  console.log('\x1b[0m', '-----------------------------------------------');

  socket.on('disconnect', function() {
    Usuarios--;
    var desconectao = '\x1b[31m' + socket.id
    console.log('Socket desconectado: ' + desconectao, '\x1b[0m');
    io.sockets.emit('users-connected', Usuarios);
    socket.removeAllListeners();
  });

  socket.on('new-publication', function(data){
    console.log('Mensaje del usuario: ' + data.pub);
    io.sockets.emit('do-it', data)
  })

  socket.on('do-publication', function(data){

    console.log('Info: ' + data.nombre);
    io.sockets.emit('do-publication', data)
  })

});

http.listen(8081, function() {
  console.log("websockets corriendo en el puerto 8081");
});

Controller.js

var mysql = require('mysql');
var io = require('socket.io-client');
var socket = io.connect('http://localhost:8081', {transports: ['websocket'], upgrade: false});


module.exports = {

    index: function(req,res,next){

        if (req.isAuthenticated()) {

            var config =require('.././database/config');
            var db = mysql.createConnection(config);

            db.query('SELECT publicaciones.publicacion, publicaciones.fecha_pub, publicaciones.imagen ,users.nombre AS nombre, users.image AS image FROM publicaciones JOIN users ON publicaciones.id_user = users.id ORDER BY fecha_pub DESC', function(err, rows, fields){

                resultado    = rows;

                var message = {
                    isAuthenticated : req.isAuthenticated(),
                    items: resultado,
                    user: req.user,
                };

                res.render('users/panel',message);

            });

            socket.on('users-connected', function(data){
                console.log('Conectado al servidor Websockets');
                console.log('Usuarios conectados: ' + data);
            });

            socket.on('do-it', function(data){
                var datos_pub;
                console.log(data.pub);
                var currentdate = new Date();
                var datetime = currentdate.getFullYear() + "-"
                + (currentdate.getMonth()+1) + "-"
                + currentdate.getDate() + " "
                + currentdate.getHours() + ":"
                + currentdate.getMinutes() + ":"
                + currentdate.getSeconds();

                var publicacion = {
                    id_user : req.user.id,
                    publicacion : data.pub,
                    imagen : null,
                    fecha_pub : datetime
                };

                function loadSelect(id) {

                    var resultado = {};
                    db.query('SELECT publicaciones.publicacion, publicaciones.fecha_pub, users.nombre AS nombre, users.image AS image FROM publicaciones JOIN users ON publicaciones.id_user = users.id where id_publicacion=' + id, function(err, rows, fields){


                        socket.emit('do-publication', {
                            nombre: rows[0].nombre,
                            publicacion: rows[0].publicacion,
                            fecha: rows[0].fecha_pub,
                            image: rows[0].image
                        });
                    });
                }

                db.query('INSERT INTO publicaciones SET ?', publicacion, function(err, rows, fields){
                    if(err) throw err;
                    //db.end();
                    datos_pub =  rows.insertId;
                    loadSelect(datos_pub);

                });
          });

        }else{

                res.render('users/signin',{
                    isAuthenticated : req.isAuthenticated(),
                    user : req.user,
                    publicacion : req.publicacion,
                    messages : req.flash('info')
                });
        }
    }
}

Jquery Script (Client Side)

$(document).ready(function(){

  var socket = io.connect('http://localhost:8081', {transports: ['websocket'], upgrade: false});

  socket.on('do-publication', function(data){
    console.log(data.image);
    var imageprofile = "http://localhost:3000/images/upload_images/"+data.image;
    var $items = $('<div id="card-container" class="grid-item"><div class="card"><img class="avatar" src="'+imageprofile+'"><div class="name">'+data.nombre+'</div><div class="date">'+data.fecha+ '</div><p class="card">'+data.publicacion+'</p></div></div>');
    $grid.prepend( $items )
    .masonry( 'prepended', $items );
  })

  socket.on('users-connected', function(data){
    console.log('Usuarios conectados: ' + data);
  })

  $('#Button-Post-Publication').click(function(e){
    e.preventDefault();
    var publication = $('#Text-Area-Publication').val();    
    socket.emit('new-publication', {pub: publication})


  })
})

Updated with Passport and Middleware

Passport.js

var LocalStrategy = require('passport-local').Strategy;
var mysql= require('mysql');
var bcrypt = require('bcryptjs');

module.exports = function(passport){

    passport.serializeUser(function(user, done){
        done(null, user);
    });

    passport.deserializeUser(function(obj,done){
        done(null,obj);
    });

    passport.use(new LocalStrategy({
        passReqToCallback: true

    }, function(req,email,password,done){

        var config = require('.././database/config');

        var db = mysql.createConnection(config);
        db.connect();

        db.query('SELECT * FROM users WHERE email = ?',email, function(err,rows,fields){
            if(err) throw err;
            db.end();
            if(rows.length > 0){

                var user = rows[0];
                if (bcrypt.compareSync(password,user.password)) {
                    return done (null,{
                        id: user.id,
                        nombre: user.nombre,
                        email: user.email,
                        image : user.image
                    });
                }
            }

            return done(null,false, req.flash('authmessage','Email o Password incorrecto'));
        });


    }
    ));

};

Middleware

module.exports ={
    isLogged:function(req,res,next){
        // si esta autentificado continua en caso contrario ira a la página de registro
        if(req.isAuthenticated()){
            next();
        }else{
            res.redirect('/');
        }
    }
}

回答1:

A page refresh kills the socket connection and creates a new one. This will happen with network outages or anything else that will break the connection.



回答2:

You don't show enough of your code to follow the whole flow, but it looks to me like you may have duplicate event handlers for the do-it message in your controllers.js file.

Each time the .index() method in controllers.js is called with an authenticated request, you will add another event handler for the do-it message in this line of code:

 socket.on('do-it', function(data){...});

That will cause you to process that message multiple times, each time it arrives, leading to duplicate database inserts.

So, you aren't actually getting duplicate messages, you are just getting the message once for a given piece of data as intended, but you have duplicate event handlers installed which each processes the message.

Usually, the answer to a problem like this is move the event handlers out of the code that happens more than once so the event handlers are only ever added once during your code initialization. It doesn't look like you can strictly do that here because you're also trying to use req in the same code. I'd have to see a greater code context for controllers.js to understand what you're actually trying to do and what a good option would be for solving.

FYI, what you've done here is mixed socket event handlers with http event handlers (put one event handler inside the other event handler). That pretty much never works as the two events happen at different times and you end up with duplicate handlers.