AWS IoT
开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 Amazon AWS 入门

协议

消息代理支持使用 MQTT 协议进行发布和订阅,并支持使用 HTTPS 协议进行发布。两个协议均通过 IP 版本 4 和 IP 版本 6 受支持。消息代理还支持基于 WebSocket 的 MQTT 协议。

协议/端口映射

下表显示 AWS IoT 支持的每个协议、身份验证方法和每个协议使用的端口。

协议、身份验证及端口映射

协议 身份验证 Port

MQTT

客户端证书

8883、443

HTTP

客户端证书

8443

HTTP

SigV4

443

MQTT + WebSocket

SigV4

443

希望通过将 MQTT 与端口 443 上的 X.509 客户端证书身份验证结合使用来进行连接的客户端必须实施 应用程序层协议协商 (ALPN) TLS 扩展并将 x-amzn-mqtt-ca 作为 ProtocolNameList 中的 ProtocolName 传递。请注意,通过将 MQTT 与端口 8883 上的 X.509 客户端证书身份验证结合使用来打开连接不需要 ALPN。

MQTT

MQTT 是专为受限设备设计的、广泛应用的轻型消息处理协议。有关更多信息,请参阅 MQTT

尽管 AWS IoT 消息代理的实施基于 MQTT 3.1.1 版,但却与规范存在如下偏差:

  • 在 AWS IoT 中,利用服务质量 (QoS) 0 订阅主题意味着消息将发送零次或多次。消息可能会多次发送。多次发送的消息在发送时可能会使用不同的数据包 ID。在这些情况下,不会设置 DUP 标志。

  • AWS IoT 不支持利用 QoS 2 进行发布和订阅。请求 QoS 2 时,AWS IoT 消息代理不会发送 PUBACK 或 SUBACK。

  • 用于发布和订阅主题的各 QoS 级别之间毫无关系。一个客户端可使用 QoS1 订阅主题,而另一个客户端可使用 QoS0 向同一主题发布消息。

  • 在响应连接请求时,消息代理将发送 CONNACK 消息。此消息包含一个标志,用于指明该连接是否会恢复上一个会话。如果两个 MQTT 客户端同时连接到同一客户端 ID,该标志的值可能会不正确。

  • 当客户端订阅主题时,在消息代理开始发送 SUBACK 和客户端开始收到新的匹配消息之间存在时间延迟。

  • MQTT 规范提供了相应的配置,以供发布者用于请求代理将发送到主题的最新消息保留下来并发送给未来的所有主题订阅者。AWS IoT 不支持保留的消息。如果请求保留消息,则将断开连接。

  • 消息代理使用客户端 ID 标识每个客户。客户端 ID 作为 MQTT 有效负载的一部分从客户端传递到消息代理。两个具有相同客户端 ID 的客户端不得同时连接到消息代理。当某个客户端使用另一客户端正在使用的客户端 ID 连接到消息代理时,系统将向两个客户端发送 CONNACK 消息,当前连接的客户端也将断开连接。

  • 消息代理不支持持久性会话 (将 cleanSession 标记设置为 false 进行的连接)。AWS IoT 消息代理假设所有会话均为干净会话,并且未在这些会话中存储消息。如果 MQTT 客户端尝试在 cleanSession 设置为 false 的情况下连接到 AWS IoT 消息代理,则客户端将断开连接。

  • 在极少数情况下,消息代理可能会使用不同的数据包 ID 再次发送相同的逻辑 PUBLISH 消息。

  • 消息代理并不保证收到消息和 ACK 的顺序。

HTTP

消息代理支持客户端通过 REST API 使用 HTTP 协议进行连接。客户端通过将 POST 消息发送到 <AWS IoT Endpoint>/<url_encoded_topic_name>?qos=1" 进行发布。

例如,您可以使用 curl 模拟按钮按压。如果您按照 AWS IoT 入门中的教程操作,而不是像在 AWS IoT MQTT 客户端中一样使用 AWS IoT MQTT 客户端发布消息,则请使用类似如下的命令:

curl --tlsv1.2 --cacert root-CA.crt --cert 4b7828d2e5-certificate.pem.crt --key 4b7828d2e5-private.pem.key -X POST -d "{ \"serialNumber\": \"G030JF053216F1BS\", \"clickType\": \"SINGLE\", \"batteryVoltage\": \"2000mV\" }" "https://a1pn10j0v8htvw.iot.us-east-1.amazonaws.com:8443/topics/iotbutton/virtualButton?qos=1"
--tlsv1.2

使用 TLSv1.2 (SSL)。必须随 OpenSSL 安装 curl,并且务必使用 TLS 1.2 版。

--cacert <filename>

用于验证对等项的 CA 证书文件名。

--cert <filename>

客户端证书文件名。

--key <filename>

私有密钥文件名。

-X POST

请求的类型,此处为 POST。

-d <data>

要发布的 HTTP POST 数据。在这种情况下,我们会模拟通过一次按钮按压发送的数据。

"https://..."

URL。此处指的是事物的 REST API 终端节点。(要查找事物的终端节点,请从 AWS IoT 控制台选择 Registry 来展开选项。选择 Things,选择事物,然后选择 Interact。)在终端节点后添加端口 (:8443),后跟主题;最后,在查询字符串 (?qos=1) 中指定服务质量。

基于 WebSocket 的 MQTT 协议

AWS IoT 支持基于 WebSocket 的 MQTT 协议,从而使基于浏览器的远程应用程序能够通过连接到 AWS IoT 的设备使用 AWS 凭证发送和接收消息。指定 AWS 凭证时使用的是 AWS 签名版本 4。WebSocket 支持服务适用于 TCP 端口 443,因此,消息可以穿过大多数防火墙和 Web 代理。

通过发送 HTTP GET 请求在客户端上启动 WebSocket 连接。您使用的 URL 应采用以下格式:

wss://<endpoint>.iot.<region>.amazonaws.com/mqtt
wss

指定 WebSocket 协议。

终端节点

特定于您的 AWS 账户的 AWS IoT终端节点。您可以使用 AWS IoT CLI describe-endpoint 命令找到该终端节点。

region

您的 AWS 账户所在的 AWS 区域。

mqtt

指定您将使用 WebSocket 协议发送 MQTT 消息。

当服务器进行响应时,客户端将发送升级请求,向服务器表明它将使用 WebSocket 协议进行通信。在服务器确认升级请求后,将使用 WebSocket 协议执行所有通信。您使用的 WebSocket 实施将充当传输协议。您使用 WebSocket 协议发送的数据是 MQTT 消息。

在 Web 应用程序中使用 WebSocket 协议

大多数 Web 浏览器提供的 WebSocket 实施不允许修改 HTTP 标头,因此,您必须在查询字符串中添加签名版本 4 信息。有关更多信息,请参阅将签名信息添加到查询字符串

以下 JavaScript 将定义在生成签名版本 4 请求时使用的一些使用程序函数。

/** * utilities to do sigv4 * @class SigV4Utils */ function SigV4Utils() {} SigV4Utils.getSignatureKey = function (key, date, region, service) { var kDate = AWS.util.crypto.hmac('AWS4' + key, date, 'buffer'); var kRegion = AWS.util.crypto.hmac(kDate, region, 'buffer'); var kService = AWS.util.crypto.hmac(kRegion, service, 'buffer'); var kCredentials = AWS.util.crypto.hmac(kService, 'aws4_request', 'buffer'); return kCredentials; }; SigV4Utils.getSignedUrl = function(host, region, credentials) { var datetime = AWS.util.date.iso8601(new Date()).replace(/[:\-]|\.\d{3}/g, ''); var date = datetime.substr(0, 8); var method = 'GET'; var protocol = 'wss'; var uri = '/mqtt'; var service = 'iotdevicegateway'; var algorithm = 'AWS4-HMAC-SHA256'; var credentialScope = date + '/' + region + '/' + service + '/' + 'aws4_request'; var canonicalQuerystring = 'X-Amz-Algorithm=' + algorithm; canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(credentials.accessKeyId + '/' + credentialScope); canonicalQuerystring += '&X-Amz-Date=' + datetime; canonicalQuerystring += '&X-Amz-SignedHeaders=host'; var canonicalHeaders = 'host:' + host + '\n'; var payloadHash = AWS.util.crypto.sha256('', 'hex') var canonicalRequest = method + '\n' + uri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash; var stringToSign = algorithm + '\n' + datetime + '\n' + credentialScope + '\n' + AWS.util.crypto.sha256(canonicalRequest, 'hex'); var signingKey = SigV4Utils.getSignatureKey(credentials.secretAccessKey, date, region, service); var signature = AWS.util.crypto.hmac(signingKey, stringToSign, 'hex'); canonicalQuerystring += '&X-Amz-Signature=' + signature; if (credentials.sessionToken) { canonicalQuerystring += '&X-Amz-Security-Token=' + encodeURIComponent(credentials.sessionToken); } var requestUrl = protocol + '://' + host + uri + '?' + canonicalQuerystring; return requestUrl; };

创建签名版本 4 请求

  1. 创建规范的签名版本 4 请求。

    以下 JavaScript 代码将创建规范请求:

    var datetime = AWS.util.date.iso8601(new Date()).replace(/[:\-]|\.\d{3}/g, ''); var date = datetime.substr(0, 8); var method = 'GET'; var protocol = 'wss'; var uri = '/mqtt'; var service = 'iotdevicegateway'; var algorithm = 'AWS4-HMAC-SHA256'; var credentialScope = date + '/' + region + '/' + service + '/' + 'aws4_request'; var canonicalQuerystring = 'X-Amz-Algorithm=' + algorithm; canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(credentials.accessKeyId + '/' + credentialScope); canonicalQuerystring += '&X-Amz-Date=' + datetime; canonicalQuerystring += '&X-Amz-SignedHeaders=host'; var canonicalHeaders = 'host:' + host + '\n'; var payloadHash = AWS.util.crypto.sha256('', 'hex') var canonicalRequest = method + '\n' + uri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash;
  2. 创建要签名的字符串,生成签名密钥,然后为该字符串签名。

    采用在上一步中创建的规范 URL 并将其组合到待签名的字符串中。为此,请创建由哈希算法、日期、凭证范围以及规范请求的 SHA 组成的字符串。然后,生成签名密钥并为该字符串签名,如以下 JavaScript 代码所示。

    var stringToSign = algorithm + '\n' + datetime + '\n' + credentialScope + '\n' + AWS.util.crypto.sha256(canonicalRequest, 'hex'); var signingKey = SigV4Utils.getSignatureKey(credentials.secretAccessKey, date, region, service); var signature = AWS.util.crypto.hmac(signingKey, stringToSign, 'hex');
  3. 将签名信息添加到请求中。

    以下 JavaScript 代码表明了如何将签名信息添加到查询字符串中。

    canonicalQuerystring += '&X-Amz-Signature=' + signature;
  4. 如果您具有会话凭证(来自 STS 服务器、AssumeRole 或 Amazon Cognito),请在签名后将会话令牌附加到 URL 的末尾:

    canonicalQuerystring += '&X-Amz-Security-Token=' + encodeURIComponent(credentials.sessionToken);
  5. 在规范查询字符串前面加上协议、主机和 URL:

    var requestUrl = protocol + '://' + host + uri + '?' + canonicalQuerystring;
  6. 打开 WebSocket。

    以下 JavaScript 代码将表明如何创建 Paho MQTT 客户端并将 CONNECT 调用到 AWS IoT 中。endpoint 参数是特定于您的 AWS 账户的终端节点。clientId 是您的 AWS 账户同时连接的所有客户端中的唯一文本标识符。

    var client = new Paho.MQTT.Client(requestUrl, clientId); var connectOptions = { onSuccess: function(){ // connect succeeded }, useSSL: true, timeout: 3, mqttVersion: 4, onFailure: function() { // connect failed } }; client.connect(connectOptions);

在移动应用程序中使用 WebSocket 协议

我们建议在建立 WebSocket 连接时使用其中一个 AWS IoT Device SDK 将您的设备连接到 AWS IoT。以下 AWS IoT 设备软件开发工具包支持基于 WebSocket 的 MQTT 连接到 AWS IoT:

有关使用基于 WebSocket 的 MQTT 协议将 Web 应用程序连接到 AWS IoT 的参考实施案例,请参阅 AWS WebSocket 实验室示例

如果您使用的是当前不受支持的编程或脚本语言,则只要使用 AWS 签名版本 4 为初始 WebSocket 升级请求 (HTTP POST) 签名,便可以使用任何现有的 WebSocket 库。有些 MQTT 客户端(如 Eclipse Paho for JavaScript)可为 WebSocket 协议提供本机支持。