2017-08-13 66 views
1

我使用Sails.js构建了一个使用NodeJS的简单API端点。NodeJS - 响应流

当有人访问我的API端点时,服务器开始等待数据,每当有新数据出现时,他都使用套接字广播它。每个客户应该根据他的用户输入接收他自己的数据流。 (我从来没有发送过res.json() - 实际发生的事情是浏览器不断加载 - 但上述功能工作)。

2个问题:

  • 我想订阅和退订从我的客户这个API端点(使用RxJS)。当我订阅时,我开始通过套接字接收数据 - 但我无法取消订阅API端点(浏览器希望完成请求)。

  • 每个客户端都应该根据请求IP参数订阅他自己的套接字空间(请参阅更新的代码)。目前它把这个信息传达给每个人。

我该如何创建一个类似Sails.js的Stream/Service-like API端点,它会根据他的输入向每个用户发送新数据?

我的目标是能够从每个客户端订阅/取消订阅此API端点。

回答

1

修改稿回答

让我们假设你的API端点在config/routes.js定义是这样的:

... 
'get  /collect': 'SomeController.collectSubscribe', 
'delete /collect': 'SomeController.collectUnsubscribe', 

由于每个Cap实例绑定到一个设备,我们需要为每个订阅一个实例。我们没有使用方法join/leave方法,而是跟踪内存中的Cap实例,并且仅对请求套接字的ID为broadcast。这是有效的,因为默认情况下,Sails套接字订阅了它们自己的ID。

api/controllers/SomeController.js

// In order for the `Cap` instances to persist after `collectSubscribe` finishes, we store them all in an Object, associated with which socket the were created for. 
var caps = {/* req.socket.id: <instance of Cap>, */}; 

module.exports = { 

... 

    collectSubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest("I need a websocket! Help!"); 
    if (!!caps[req.socket.id]) return res.badRequest("Dude, you are already subscribed."); 

    caps[req.socket.id] = new Cap(); 
    var c = caps[req.socket.id]; // remember that `c` is a reference to our new `Cap`, not a copy. 
    var device = c.findDevice(req.param('ip')); 

    c.open(device, ...); 
    c.on('data', function(myData) { 
     sails.sockets.broadcast(req.socket.id, 'message', {host: myData}); 
    }); 

    return res.ok(); 
    }, 

    collectUnsubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest("I need a websocket! Help!"); 
    if (!caps[req.socket.id]) return res.badRequest("I can't unsubscribe you unless you actually subscribe first."); 

    caps[req.socket.id].removeAllListeners('data'); 
    delete caps[req.socket.id]; 

    return res.ok(); 
    } 
} 

基本上,它是这样的:当浏览器请求触发collectSubscribe,新Cap实例监听提供的IP。当浏览器触发collectUnsubscribe时,服务器会检索该实例,告诉其停止监听,然后将其删除。

生产注意事项:请注意,Cap s列表不是数据库持久存储器(因为它存储在内存中而不是数据库)!因此,如果您的服务器关闭并重新启动(由于雷雨天气等),清单将被清除,但考虑到所有websocket连接都将被丢弃,我不认为有任何必要担心这一点。

老回答,不停地为参考

您可以使用sails.sockets.join(req, room)sails.sockets.leave(req, room)管理插座的房间。基本上你有一个叫做“collect”的房间,只有加入该房间的套接字才会收到sails.sockets.broadcast(room, eventName, data)

有关如何给用户的信息sails.socketshere

api/controllers/SomeController.js

collectSubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest(); 

    sails.sockets.join(req, 'collect'); 
    return res.ok(); 

}, 

collectUnsubscribe: function(req, res) { 
    if (!res.isSocket) return res.badRequest(); 

    sails.sockets.leave(req, 'collect'); 
    return res.ok(); 
} 

最后,我们需要告诉服务器发送广播消息到我们的房间'collect'。 请注意,这只需要发生一次,因此您可以在config/目录下的文件中执行此操作。

在这个例子中,我把这个config/sockets.js

module.exports = { 
    // ... 
}; 


c.on('data', function(myData) { 
    var eventName = 'message'; 
    var data = {host: myData}; 
    sails.sockets.broadcast('collect', eventName, data); 
}); 

我假设c在这里访问;如果没有,您可以将其定义为sails.c = ...以使其可全局访问。

+0

首先,感谢一个真棒的答案!我有一个问题:目前我必须使用从请求'(req.param(“ip”))''得到的参数来初始化'c',并且每个用户需要在他自己的房间中。 OK('data')'根据用户输入发出新数据 – TheUnreal

+0

好的,哇,这是我设想的不同的最终目标!如果你可以用这些具体的细节来编辑你的问题,这将有很大的帮助。另外,我想看看'c'是如何声明的,因为它现在看起来与这个问题更相关。谢谢! – ContinuousLoad

+0

对于混淆感到抱歉,只有在我看到您的答案后才提出来。我相应地更新了我的问题 – TheUnreal