【问题标题】:Separating file server and socket.io logic in node.js在 node.js 中分离文件服务器和 socket.io 逻辑
【发布时间】:2012-03-31 09:09:19
【问题描述】:

我对 node.js 还很陌生,我发现随着项目规模的扩大,将一个项目分成多个文件非常复杂。我有一个大文件,之前它既用作文件服务器,又用作多人 HTML5 游戏的 Socket.IO 服务器。理想情况下,我希望将文件服务器、socket.IO 逻辑(从网络读取信息并将其写入带有时间戳的缓冲区,然后将其发送给所有其他玩家)和游戏逻辑。

使用socket.io中的第一个示例来演示我的问题,通常有两个文件。 app.js 是服务器,index.html 被发送到客户端。

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>

为了分离文件服务器和游戏服务器逻辑,我需要在一个文件中定义函数“处理程序”,我需要使用 io.sockets.on() 回调的匿名函数在另一个文件中,我会还需要第三个文件才能成功包含这两个文件。现在我尝试了以下方法:

start.js:

var fileserver = require('./fileserver.js').start()
  , gameserver = require('./gameserver.js').start(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;
    }
}

游戏服务器:

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);
    }

}

这似乎有效(静态内容已正确提供,并且在客户端连接时控制台清楚地显示了与 Socket.IO 的握手),尽管从未发送过任何数据。就好像 socket.emit() 和 socket.on() 从来没有真正被调用过。我什至修改了 gameserver.js 中的 handler() 以添加 console.log('User connected'); 但这从未显示。

我怎样才能在一个文件中拥有 Socket.IO,在另一个文件中拥有文件服务器,并且仍然期望两者都能正常运行?

【问题讨论】:

  • 你知道express js框架吗? expressjs.com 这很棒,真的可以帮助您构建应用程序。 github 上有大量示例 (github.com/visionmedia/express/tree/master/examples) 也许有一些东西可以帮助您解决问题...
  • @pkyeck:我现在正在阅读 expressjs,试图弄清楚它如何使我受益,但到目前为止,它似乎比我需要的要复杂。我真正想要的只是将游戏服务器和文件服务器的逻辑分成两个单独的文件,然后有第三个文件可以正确启动两个服务器。
  • 您有时间查看我的“新”答案吗?
  • @pkyeck 我看过它,虽然它似乎只是掩盖了问题而不是解决它。不是 sockets.js 是 ONE MASSIVE FILE 而不是 app.js。我不想有一个单一的大文件,而是为每个功能单独的文件。越来越多的 node.js 看起来更像是一件麻烦事而不是祝福。

标签: node.js socket.io


【解决方案1】:

在 socket.io 0.8 中,您应该使用 io.sockets.on('...') 附加事件,除非您使用命名空间,否则您似乎缺少 sockets 部分:

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

最好避免以这种方式链接它(稍后您可能想使用io 对象)。我现在这样做的方式:

// 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
}

然后创建服务器后app:

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

【讨论】:

  • 很好的答案,尝试将其移植到 krakenJS 但 socket.io 模块永远不会启动:/
  • 我们没有使用'return io'对吗?它仅适用于 var io。
  • 如果我想触发一个发射请求?例如,app.get('/some/url',function(req,res){ // I want to emit here })
【解决方案2】:

我会做这样的事情。

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);

和 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]);
                                }


                        });
                });
        }
};

我只是复制并粘贴了一些旧代码 - 不知道 socket.io 的最新版本发生了什么变化,但这更多是关于结构而不是实际代码。

我只会为您的目的使用 2 个文件,而不是 3 个。 当您考虑将其进一步拆分时,可能是针对不同路线的另一个文件...

希望这会有所帮助。

【讨论】:

  • 现在它只是一个文件服务器和一个 socket.io 服务器,但最终我还将有游戏逻辑来确定给定移动更新的玩家位置,并且我需要有延迟最小化逻辑来查看ping每个客户端,并根据现有数据估计他们当前处于哪个游戏状态。有一些快进时间的逻辑,一些倒带时间的逻辑,一些移动播放器的逻辑,一些处理网络数据的逻辑,以及一些处理文件的逻辑,这意味着我想把所有东西分成不同的文件,理想情况下。不仅仅是 socket.io 的东西。
  • 这只是一个开始 - 您发布的文件。您可以做多个var xxx = require('./xxx'); 并将您的应用程序拆分为多个文件。我昨天参加了 mongodb 会议,来自 10gen 的某个人展示了一个基于 node/mongo/websockets (github.com/christkv/mongoman) 的游戏,他通过套接字发送 BSON 数据并解码客户端上的数据 - 使客户端/服务器之间的通信更快...也许这对你来说很有趣!?
【解决方案3】:

我也对此有所了解,我对结果相当满意。查看https://github.com/hackify/hackify-server 获取源代码。

【讨论】:

    【解决方案4】:

    我有另一个解决方案。您可以使用 require.js 创建一个模块并将“app”作为参数传递。在模块中,您可以启动 socket.io 并组织您的套接字。

    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
    
      });
    

    在您的 socket.js 模块中,您可以执行以下操作:

      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;
        }
      });
    

    希望对您的贡献有所帮助。

    【讨论】:

    • 万一读到这篇文章的人想知道,不,根本没有理由在节点中使用 AMD。
    • 这只是一种选择,不是唯一的方法
    猜你喜欢
    • 1970-01-01
    • 2015-02-09
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 2014-09-04
    相关资源
    最近更新 更多