65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Emitter 进行 IoT、移动或 Web 设备在线状态跟踪

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016 年 8 月 22 日

MIT

4分钟阅读

viewsIcon

16095

演示如何使用 emitter.io 平台跟踪设备或用户的在线状态

引言

本文介绍了一种跟踪各种设备(如物联网设备、服务器甚至 Web 会话)存在状态的方法。通常有必要查询一组此类设备的状态,并查看当前有多少设备已连接并正在传输数据。

[查看实时演示]

在本例中,我们将构建一个小的 Web 应用程序,用于跟踪已登录用户的状态,为了简单起见,我们将在同一页面内创建 3 个不同的连接。值得注意的是,该技术不仅限于 Web,并且底层使用 MQTT 协议,因此此处描述的所有代码都可以轻松地用 C/C++ 编写并在低内存集成板上运行。

背景

在本教程中,我们将使用名为 emitter.io 的服务来处理在线状态。状态功能已集成到整个服务中,并且对用户是透明的。在 emitter 中,有两种获取状态信息的方法,它们是互补的

  • 检索当前在线设备的列表,这是一个请求/响应样式的 API 调用,并返回一条状态消息,描述订阅者数量及其客户端标识符。
  • 加入/离开更新通知,当设备订阅或取消订阅特定的 MQTT 频道或通配符时,由服务推送。

现在,emitter 是一个分布式的、发布-订阅MQTT 代理。在本文中,我们假设您对 MQTT 有一些基本了解,并且我们不会详细介绍协议的规范以及如何使用它,但是,这里有几点重要说明

  • 在 MQTT 中,客户端可以订阅主题(频道),这些主题由分层字符串表示(例如:sensor/1/temperature/)。
  • 客户端可以向这些频道发布任意二进制数据。
  • MQTT 数据包的标头只有 2 个字节,并且存在各种客户端库。

对状态信息感兴趣的设备

假设我们有许多订阅特定频道的 MQTT 客户端(连接)。在我们的演示中,频道是 "presence-demo/" + Math.random().toString(16).substr(2, 8),它为每个人创建了一个隔离的演示。我们将检索

  1. 订阅该频道的客户端,以及
  2. 当新客户端订阅该频道或现有客户端取消订阅该频道时的通知

在该示例中,我们首先创建将订阅通知的主连接。请注意,在这种情况下我们使用 secure: true 参数,因为我们希望我们的设备通过 TLS/SSL 连接。

var client0 = emitter.connect({ secure: true });

一旦我们希望监视状态的客户端连接上,我们查询该频道的状态。

client0.on('connect', function(){
    // Query the presence state
    client0.presence({
        key: key,
        channel: channel
    })
});

当我们第一次调用我们的 presence() 函数时,我们会收到如下格式的响应,告诉我们当前没有人订阅 occupancy: 0。由于我们没有另行指定,client0 也订阅了此频道的加入/离开推送通知,并且 emitter 代理将在这些事件发生时将此类事件转发给我们。

{event: "status", channel: "presence-demo/8758f6d3/", occupancy: 0, time: 1471850444, who: []}

要监视的设备

接下来,我们创建了几个我们将要测试的客户端。最初,这些连接被禁用。

var client1 = null;
var client2 = null;

我们将打开和关闭这些连接以模拟设备连接和断开连接。在这种情况下,name 参数实际上将设置 MQTT 的 clientId 选项,从而允许我们识别各种设备。由于我们控制了 clientId,我们也可以将任何状态与它们关联起来。在示例中,我们仅给用户一些名称,但理想情况下,可以使用唯一的标识符,例如 GUID/UUID 或强伪随机 string

/**
 * Function that toggles the connection on a particular emitter client.
 */
function toggleConnection(client, name) {
    if(client) {
        // If client is already connected, disconnect it
        client.disconnect();
        return null;
    } else {
        // If client is not yet connected, connect and subscribe to the channel
        client = emitter.connect({ secure: true, clientId: name });
        client.on('connect', function(){
            client.subscribe({
                key: key,
                channel: channel
            });
        });
        return client;
    }
}

当一个客户端加入时,我们会收到一个 subscribe 事件,其中包含当前占用率、时间戳(以 UNIX 时间为单位)和客户端 ID。在此演示中,我们将客户端 ID 设置为用户名,但理想情况下,您将拥有一个 GUID/UUID,如前所述。

{event: "subscribe", 
channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850449, who: "Alan"}

另一个用户可能会订阅,我们将收到另一个事件。

{event: "subscribe", 
channel: "presence-demo/8758f6d3/", occupancy: 2, time: 1471850450, who: "Margaret"}

当用户取消订阅时,我们会收到一个 unsubscribe 事件以及当前的占用率。

{event: "unsubscribe", 
channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850453, who: "Alan"}

现在,订阅状态事件的客户端/设备将接收并处理各种通知。请参阅下面的代码段,它非常简单。在这种情况下,它使用 vuejs 数据绑定更新我们的 UI。

// on every presence event, print it out
client0.on('presence', function(msg){
    console.log(msg);    
    var users = vue.$data.users;
    switch(msg.event){
        // Occurs when we've received a full response with a complete list of clients
        // that are currently subscribed to this channel. 
        case 'status':
            for(var i=0; i<msg.who.length;++i){
                users.push({
                    name: msg.who[i]
                });
            }
        break;

        // Occurs when a user subscribes to a channel.
        case 'subscribe':
            users.push({ 
                name: msg.who
            });
        break;

        // Occurs when a user unsubscribes or disconnects from a channel.
        case 'unsubscribe':
            vue.$data.users = users.filter(function( obj ) {
                return obj.name !== msg.who;
            });
        break;
    }

    // Also, set the occupancy
    vue.$data.occupancy = msg.occupancy;
});

延伸阅读

这就是使用 emitter 的状态。最后,您可能需要查看更多资源,包括一些演示和源代码,以获取演示状态功能的完整应用程序。

历史

  • 2016 年 8 月 22 日 - 文章的初始版本
© . All rights reserved.