Node.js&带有多个标签的Socket.io

时间:2016-07-14 01:37:17

标签: javascript node.js socket.io

根据我的理解,socket.io服务器通过N个独立的套接字管理自身与N个客户端之间的N个连接,每个套接字都有自己的ID。

因此,打开两个标签时,您可以获得两个不同的ID。

然而,请考虑这个最小的例子:

的index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"></html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <title>Stuffy Stuff</title>
</head>

<body>
    <button id="btnA">A</button>

    <script src="/socket.io/socket.io.js"></script>
    <script src="app.js"></script>
</body>

index.js

var express = require('express');
var path = require('path');
var app = express();
var servercode = require('./server');

app.use(express.static(path.join(__dirname,'public')));

var server = require('http').createServer(app).listen(process.env.PORT || 8080);
var io = require('socket.io').listen(server);

io.on('connection', function (socket)
{
    console.log('client ' + socket.id + ' connected');
    servercode.init(io, socket);
});

app.js

;
jQuery(function($) // client-side code
{
    socket = io.connect();

    $(document).on('click', '#btnA', stuff);

    function stuff()
    {
        socket.emit('dostuff', socket.id);
        socket.on('answerstuff', function()
        {
            console.log("Answered");
        });
    }
}($));

server.js

// Server-side

var io;
var socket;

exports.init = function(sio, sock)
{
    io = sio;
    socket = sock;

    socket.on('dostuff', doStuff);
};

function doStuff(idd)
{
    console.log('who is asking: ' + idd);
    console.log('who am I answering to: ' + socket.id);  
    console.log(socket.id);
    socket.emit('answerstuff');
}

现在说我启动node index.js,然后打开localhost:8080的两个实例;让我们称他们为A和B.如果我按下A&A实例上的按钮,那就是&#39; dostuff&#39;消息被发出,它被服务器捕获,但它回答的套接字是相对于B实例的套接字,而不是A&#39。一般来说,它回答了最新的实例。为什么会这样?

编辑:解释 当我终于掌握了我的错误的本质时,我想我可以帮助遇到类似问题的人。

让我们来看看我们的示例工作流程:A连接,B连接,A按下按钮。

  1. server.init(io, socket A)被称为
  2. init(socket A)将带有回调函数dostuff的{​​{1}}事件附加到套接字A
  3. B连接... doStuffinit附加到套接字B
  4. 客户端A按下按钮,因此A发出dostuff通过套接字A的ID
  5. dostuff已执行,但其定义包含对未在函数范围内定义的变量(doStuff(idd))的引用,因此必须导航其父作用域
  6. 找到了这样的引用:socket(如果我们省略socket = sock,声明会在范围内冒泡,直到找到具有此名称的变量为止如果我们写了var INSIDE var socket = sock,那么函数就找不到任何这样的变量了)
    1. 但由于B连接和init再次执行,socket指向套接字B.因此,该函数只知道套接字B。
  7. 为了解决这个问题,我们需要使用闭包:在定义时封装世界状态的函数。换句话说,闭包是一个包含函数定义的对象和不在函数作用域中但由它引用的变量的值。

    正如JagsSparrow给出的解决方案所示,有两种方法可以解决这个问题,并且都涉及创建一个闭包:

    1。使用init功能

    function.bind(thisArg, args)函数返回函数&#39; A&#39;调用bind()绑定到this的位置(通常指向调用A的对象)并实例化thisArg中指定的任何参数。

    在我们的案例中,我们不需要args,因为它在this函数中从未提及,但我们确实需要它来记住哪个套接字是在参数。我们可以通过写

    来做到这一点
    doStuff()

    此表达式返回一个doStuff.bind(null, socket) 函数的对象,在其上下文中doStuff()等于this(我们可以写null:在那里case,doStuff.bind(this, socket)等于this),其第一个参数init绑定到socket。因此,

    socket

    告诉套接字A在socket.on('dostuff', doStuff.bind(this,socket)); 发生时触发doStuff包含对套接字A的引用)。与套接字B和任何其他套接字相同。

    2。使用嵌套函数

    我们只是在dostuff内移动function doStuff(idd)的定义:

    socket.on()

    这样,套接字再次绑定到一个函数,该函数包含(通过闭包)任何执行此操作的套接字的定义。

1 个答案:

答案 0 :(得分:1)

server.js中,您声明var socket; 本地服务器.js模块(模块范围)

因此,每次连接时,此变量都会分配给不同的客户端

  1. 当客户端A连接socket时,客户端A。
  2. 当客户端B连接时socket是客户端B。
  3. 所以,不要将socket变量置于server.js模块的本地变量

    解决方案1: server.js

    var io;
    //var socket;
    
    exports.init = function(sio, sock)
    {
        io = sio;
        socket = sock;
    
        socket.on('dostuff', doStuff.bind(this,socket));
    };
    
    function doStuff(socket,idd)
    {
        console.log('who is asking: ' + idd);
        console.log('who am I answering to: ' + socket.id);  
        console.log(socket.id);
        socket.emit('answerstuff');
    }
    

    解决方案2: server.js

    var io;
    //var socket;
    
    exports.init = function(sio, sock)
    {
        io = sio;
        var socket = sock;
    
        socket.on('dostuff', function doStuff(idd)
        {
            console.log('who is asking: ' + idd);
            console.log('who am I answering to: ' + socket.id);  
            console.log(socket.id);
            socket.emit('answerstuff');
        });
    };