Skip to main content

如何处理Azure IoT Hub客户端的连接状态改变和重连

分类:  Azure物联网 标签:  #Azure #IoT Hub # 发布于: 2023-06-13 21:00:30

前面我们学习了很多在.Net Device SDK上处理消息的情况,我们这里来总结一下:

连接到Azure IoT Hub

连接到Azure IoT Hub首先需要考虑几个要素:

  • 连接认证的方式
  • 连接的字符串
  • 连接使用的协议

关于连接的认证方式,我们之前已经学习过了,基本上有三种:

  • 使用SAS Key连接
  • 使用CA签发的证书连接
  • 使用自签发证书的指纹连接

我们之前已经有代码的例子,大家可以找来试一试。

连接字符串可以使用Azure Cli的IoT扩展轻松获得,或者从Azure Portal上可以取得该连接字符串。

连接可以使用的协议:

  • MQTT_WebSocket_only
  • MQTT_TCP_only
  • AMQP
  • AMQP_WebSocket_Only
  • AMQP_Tcp_only
  • http

连接的Demo

string connectionString = "<connection_string>";
TransportType transportType = TransportType.Mqtt;

// This option is helpful in delegating the assignment of Message.MessageId to the sdk.
// If the user doesn't set a value for Message.MessageId, the sdk will assign it a random GUID before sending the message.
var options = new ClientOptions
{
    SdkAssignsMessageId = Shared.SdkAssignsMessageId.WhenUnset,
};
deviceClient = DeviceClient.CreateFromConnectionString(connectionString, transportType, options);

超级有意思的是ClientOptions这个类,需要我们格外的关注下。

发送遥测数据到云

直接看代码:

var temperature = 25;
var humidity = 70;
string messagePayload = $"{"temperature :{temperature}, humidity :{humidity}";

using var eventMessage = new Message(Encoding.UTF8.GetBytes(messagePayload))
{
    ContentEncoding = Encoding.UTF8.ToString(),
    ContentType = "application/json",
};

await deviceClient.SendEventAsync(message);

主要是理清一个消息的是如何生成的。

在设备上是pull的方式接受消息

// This snippet shows you how to call the API for receiving telemetry sent to your device client.
// In order to ensure that your client is resilient to disconnection events and exceptions, refer to https://github.com/Azure-Samples/azure-iot-samples-csharp/blob/main/iot-hub/Samples/device/DeviceReconnectionSample/DeviceReconnectionSample.cs.
using Message receivedMessage = await deviceClient.ReceiveAsync();
if (receivedMessage == null)
{
    Console.WriteLine("No message received; timed out.");
    return;
}

string messageData = Encoding.ASCII.GetString(receivedMessage.GetBytes());
var formattedMessage = new StringBuilder($"Received message: [{messageData}]\n");

// User set application properties can be retrieved from the Message.Properties dictionary.
foreach (KeyValuePair<string, string> prop in receivedMessage.Properties)
{
    formattedMessage.AppendLine($"\tProperty: key={prop.Key}, value={prop.Value}");
}

// System properties can be accessed using their respective accessors.
formattedMessage.AppendLine($"\tMessageId: {receivedMessage.MessageId}");

Console.WriteLine(formattedMessage.ToString());
await deviceClient.CompleteAsync(receivedMessage);

注意从设备上使用拉取的方式获取消息,要记得CompleteAsync

在设备上接受C2D消息

// This snippet shows you how to call the API for receiving telemetry sent to your device client.
// In order to ensure that your client is resilient to disconnection events and exceptions,
// refer to https://github.com/Azure-Samples/azure-iot-samples-csharp/blob/main/iot-hub/Samples/device/DeviceReconnectionSample/DeviceReconnectionSample.cs.
private async Task OnC2dMessageReceived(Message receivedMessage, object userContext)
{
    string messageData = Encoding.ASCII.GetString(receivedMessage.GetBytes());
    var formattedMessage = new StringBuilder($"Received message: [{messageData}]\n");

    // User set application properties can be retrieved from the Message.Properties dictionary.
    foreach (KeyValuePair<string, string> prop in receivedMessage.Properties)
    {
        formattedMessage.AppendLine($"\tProperty: key={prop.Key}, value={prop.Value}");
    }

    // System properties can be accessed using their respective accessors.
    formattedMessage.AppendLine($"\tMessageId: {receivedMessage.MessageId}");

    Console.WriteLine(formattedMessage.ToString());
    await deviceClient.CompleteAsync(receivedMessage);
}

// Subscribe to the receive message API.
await deviceClient.SetReceiveMessageHandlerAsync(OnC2dMessageReceived, userContext);

// Once you are done receiving telemetry messages sent to your device client,
// you can unsubscribe from the receive callback by setting a null handler.
await deviceClient.SetReceiveMessageHandlerAsync(null, null);

在设备上接受孪生设备预期属性更新的通知和更新设备上报属性

// This snippet shows you how to call the APIs for receiving twin desired property update notifications sent to your device client
// and sending reported property updates from your device client.
// In order to ensure that your client is resilient to disconnection events and exceptions,
// refer to https://github.com/Azure-Samples/azure-iot-samples-csharp/blob/main/iot-hub/Samples/device/DeviceReconnectionSample/DeviceReconnectionSample.cs.
private async Task HandleTwinUpdateNotificationsAsync(TwinCollection twinUpdateRequest, object userContext)
{
    _logger.LogInformation($"Twin property update requested: \n{twinUpdateRequest.ToJson()}");

    // For the purpose of this sample, we'll blindly accept all twin property write requests.
    var reportedProperties = new TwinCollection();
    foreach (KeyValuePair<string, object> desiredProperty in twinUpdateRequest)
    {
        _logger.LogInformation($"Setting property {desiredProperty.Key} to {desiredProperty.Value}.");
        reportedProperties[desiredProperty.Key] = desiredProperty.Value;
    }

    // The device app usually responds to a twin desired property update notification by sending a reported property update.
    await deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
}

// Subscribe to the twin desired property update API.
await deviceClient.SetDesiredPropertyUpdateCallbackAsync(HandleTwinUpdateNotificationsAsync, userContext);

// Once you are done receiving twin desired property update notifications sent to your device client,
// you can unsubscribe from the callback by setting a null handler.
await deviceClient.SetDesiredPropertyUpdateCallbackAsync(null, null);

需要注意的是孪生设备预期属性和设备上报属性这两一般情况下都是成对使用。

之前的要点回顾得差不多了,现在我们需要考虑设备连接状态改变得事件以及改变得原因,另外对于这些连接状态改变,我们需要怎么处理他们。

客户端的连接状态改变以及改变的原因

我们看一些例子,用于模拟设备重连:

  • 拔掉网线: 会立刻引发瞬时网络异常,同时SDK会进行重试。
  • 对连接中的设备所使用的SAS key进行更新,这会引发客户端返回状态:Disconnected, 同时标示状态更改的原因为:Bad_Credential, 这种情况下在客户端实例上运行操作会返回异常UnauthorizedException, 同时SDK不会重试。
  • Azure IoT Hub上禁止设备或者删除设备,设备SDK状态会变为Disconnected,同时状态更改的原因变为Device_Disabled, 任何在实例上的操作会返回异常DeviceNotFoundException, 同时SDK不会重试。

关于设备端的连接状态,我们可以看一下下面的表:


注意:

  • 如果设备状态是Connteced, 可以继续发送命令
  • 如果设备状态是Disconnected_Retrying, SDK在重试
  • 如果设备状态是Disconnected或者Disabled, 协议侧已经释放了,那么也需要释放client实例,重新实例化。

如何实设计一个强大的拥有自动回复重连的系统

我们下一篇来根据github上的例子重写一遍,然后我们也来学习一下。