78 votes

Séparation de la logique du serveur de fichiers et de socket.io dans node.js

Je suis assez novice en matière de node.js et j'ai constaté qu'il est assez compliqué de séparer un projet en plusieurs fichiers au fur et à mesure que la taille du projet augmente. Avant, j'avais un gros fichier qui servait à la fois de serveur de fichiers et de serveur Socket.IO pour un jeu HTML5 multi-joueurs. Idéalement, je veux séparer le serveur de fichiers, la logique Socket.IO (lire des informations sur le réseau et les écrire dans un tampon avec un horodatage, puis les transmettre à tous les autres joueurs), et la logique du jeu.

En utilisant le premier exemple de socket.io pour démontrer mon problème, il y a deux fichiers normalement. app.js est le serveur et index.html est envoyé au client.

app.js :

var app = require('http').createServer(handler)
  , io = require('socket.io').listen(app)
  , fs = require('fs')

app.listen(80);

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

index.html :

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

Pour séparer la logique du serveur de fichiers de celle du serveur de jeu, il faudrait que la fonction "handler" soit définie dans un fichier, que la fonction anonyme utilisée comme callback pour io.sockets.on() soit dans un autre fichier, et qu'un troisième fichier soit inclus avec succès dans ces deux fichiers. Pour l'instant, j'ai essayé ce qui suit :

start.js :

var fileserver = require('./fileserver.js').start()
  , gameserver = require('./gameserver.js').start(fileserver);

fileserver.js :

var app = require('http').createServer(handler),
    fs = require('fs');

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

module.exports = {
    start: function() {
        app.listen(80);
        return app;
    }
}

serveur de jeux :

var io = require('socket.io');

function handler(socket) {
    socket.emit('news', { hello: 'world' });
    socket.on('my other event', function (data) {
        console.log(data);
    });
}

module.exports = {

    start: function(fileserver) {       
        io.listen(fileserver).on('connection', handler);
    }

}

Cela semble fonctionner (le contenu statique est correctement servi et la console montre clairement une poignée de main avec Socket.IO lorsque le client se connecte) mais aucune donnée n'est jamais envoyée. C'est comme si socket.emit() et socket.on() n'étaient jamais appelés. J'ai même modifié handler() dans gameserver.js à ajouter console.log('User connected'); mais il n'est jamais affiché.

Comment puis-je avoir Socket.IO dans un fichier, un serveur de fichiers dans un autre, et m'attendre à ce que les deux fonctionnent correctement ?

4 votes

Connaissez-vous le framework express js ? expressjs.com il est génial et vous aide vraiment à structurer votre application. il y a une tonne d'exemples sur github ( github.com/visionmedia/express/tree/master/examples ) peut-être qu'il y a quelque chose qui peut vous aider avec votre problème...

1 votes

@pkyeck : Je suis en train de lire sur expressjs pour essayer de comprendre comment il peut m'être utile, mais jusqu'à présent il semble plus compliqué que ce dont j'ai besoin. Tout ce que je veux vraiment, c'est séparer ma logique pour le serveur de jeux et le serveur de fichiers en deux fichiers distincts, puis avoir un troisième fichier qui démarre correctement les deux serveurs.

0 votes

Avez-vous eu le temps de vérifier ma "nouvelle" réponse ?

95voto

Ricardo Tomasi Points 13398

Dans socket.io 0.8, vous devez attacher des événements en utilisant io.sockets.on('...') à moins que vous n'utilisiez des espaces de noms, il semble que vous manquiez l'élément suivant sockets partie :

io.listen(fileserver).sockets.on('connection', handler)

Il est probablement préférable d'éviter de l'enchaîner de cette manière (vous pouvez utiliser la fonction io plus tard). La façon dont je fais ça en ce moment :

// sockets.js
var socketio = require('socket.io')

module.exports.listen = function(app){
    io = socketio.listen(app)

    users = io.of('/users')
    users.on('connection', function(socket){
        socket.on ...
    })

    return io
}

Puis, après avoir créé le serveur app :

// main.js
var io = require('./lib/sockets').listen(app)

2 votes

Excellente réponse, j'essaie de porter cela sur krakenJS mais le module socket.io ne démarre jamais :/.

1 votes

Nous n'utilisons pas 'return io', n'est-ce pas ? C'est juste pour la var io.

3 votes

Si je veux déclencher une demande d'émission ? par exemple.., app.get('/some/url',function(req,res){ // I want to emit here })

6voto

pkyeck Points 6158

Je ferais quelque chose comme ça.

app.js

var app = require('http').createServer(handler),
    sockets = require('./sockets'),
    fs = require('fs');

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

sockets.startSocketServer(app);
app.listen(80);

et sockets.js

var socketio = require('socket.io'),
        io, clients = {};

module.exports = {

        startSocketServer: function (app) {
                io = socketio.listen(app);

                // configure
                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 disconnected");

                        });

                        socket.on('connect_device', function (data, fn) {
                                console.log("data from connected device: " + data);
                                for (var col in data) {
                                        console.log(col + " => " + data[col]);
                                }

                        });
                });
        }
};

Je viens de copier-coller une partie de mon ancien code - je ne sais pas vraiment ce qui a changé dans les dernières versions de socket.io, mais il s'agit plus de la structure que du code lui-même.

et je n'utiliserais que 2 fichiers pour vos besoins, pas 3. si vous pensez à le diviser davantage, peut-être un autre fichier pour les différentes routes ...

J'espère que cela vous aidera.

0 votes

Pour l'instant, il s'agit simplement d'un serveur de fichiers et d'un serveur socket.io, mais à terme, j'aurai aussi une logique de jeu pour déterminer la position des joueurs en fonction des mises à jour des mouvements et j'aurai besoin d'une logique de minimisation du temps de latence qui regarde le ping de chaque client et estime l'état de jeu dans lequel il se trouve actuellement à partir des données existantes. Avoir une logique pour avancer rapidement le temps, une logique pour revenir en arrière, une logique pour déplacer les joueurs, une logique pour gérer les données réseau et une logique pour gérer les fichiers signifie que je voulais séparer TOUT dans différents fichiers, idéalement. Pas seulement les trucs de socket.io.

0 votes

C'est juste le début - avec les fichiers que vous avez postés. vous pouvez en faire plusieurs var xxx = require('./xxx'); et divisez votre application en plusieurs fichiers. J'étais à la conférence mongodb hier et quelqu'un de 10gen a montré un jeu basé sur node/mongo/websockets ( github.com/christkv/mongoman ) il envoie des données BSON sur le socket et décode les données sur le client - ce qui permet une communication plus rapide entre le client et le serveur ... peut-être est-ce intéressant pour vous ?

1voto

Michael Dausmann Points 985

J'ai également tenté de le faire et je suis assez satisfait du résultat. Regardez https://github.com/hackify/hackify-server pour le code source.

0voto

Marco Godínez Points 529

J'ai une autre solution. Vous pouvez utiliser require.js pour créer un module et passer "app" comme argument. Dans le module, vous pouvez démarrer socket.io et organiser vos sockets.

app.js :

  var requirejs = require('requirejs');

  requirejs.config({
      baseUrl: './',
      nodeRequire: require
  });

  requirejs(['sockets'], function(sockets) {

    var app = require('http').createServer()
      , fs  = require('fs')
      , io  = sockets(app);

      // do something
      // add more sockets here using "io" resource

  });

Dans votre socket.js vous pouvez faire quelque chose comme ça :

  define(['socket.io'], function(socket){
    return function(app){
      var server = app.listen(3000) 
        , io     = socket.listen(server);

      io.sockets.on('connection', function (socket) {
        console.log('connected to socket');

        socket.emit('news', { hello: 'world' });
        socket.on('my other event', function (data) {
          console.log(data);
        });

        // more more more

      });

      return io;
    }
  });

J'espère vous aider avec ma contribution.

2 votes

Au cas où quelqu'un qui lit ceci se poserait la question, non, il n'y a aucune raison d'utiliser AMD dans un nœud.

1 votes

C'est juste une alternative, ce n'est pas la seule façon de faire.

-1voto

Mustafa Points 2782

Vous devez absolument utiliser un framework web comme express. Ce n'est pas aussi compliqué que vous le pensez, et en fait c'est vraiment plus propre que votre solution. Voici la manière simple de servir des fichiers statiques dans un serveur web avec express :

var app = require('express').createServer();
app.use(express.static(__dirname + '/public'));
app.listen(80); 

C'est très simple et plus facile à maintenir que votre solution, il n'est pas nécessaire de gérer les fichiers indésirables ou chaque fichier séparément. Il servira tout ce qui se trouve dans le répertoire public.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X