IoT 设备流式传输未连接

IoT Device Streaming is not connecting

我正在探索 Azure IoT Hub Device Streams 功能。

我有一个 C# 客户端连接到 IoT 中心(位于美国中部)并且处于活动状态。

客户端正在使用以下算法等待传入连接(使用 SDK 版本 1.29.0-preview-004):

var buffer = new byte[1024];

using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));

DeviceStreamRequest streamRequest = await deviceClient.WaitForDeviceStreamRequestAsync(cancellationTokenSource.Token);

if (streamRequest is null)
    return;

在服务器端,我有一个在特定端点上调用的 Azure 应用服务。它检索 IoT 中心的连接字符串

ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(Constants.IoTHub.ConnectionString, TransportType.Amqp);

DeviceStreamRequest deviceStreamRequest = new DeviceStreamRequest("portal");

DeviceStreamResponse result = await serviceClient.CreateStreamAsync(serialNumber, deviceStreamRequest);

我无法建立连接。 CreateStreamAsync 调用在 1 分钟后超时,客户端似乎永远无法退出 WaitForDeviceStreamRequestAsync 调用。

我已按照 troubleshooting guide 了解发生了什么,但我什么也没得到:

问题

为什么我遗漏了会阻止 IoT 设备流式传输连接的内容?

请注意,Azure IoT 中心设备流功能仍处于 public 预览阶段(超过一年)。 您在设备端使用 SDK 1.29.0-preview-004,在服务端使用 1.27.0-preview-004,示例 DeviceStreamingSample 来自 azure-iot-samples-csharp-master 包。

设备流功能用于服务和设备之间的握手过程,与调用设备方法的通信概念相同。可以使用 REST POST 请求轻松测试此握手阶段。

出于演示目的,我将使用我的 IoT Hub Tester,其中已实现设备流功能,请参阅附录 A2 部分中的更多详细信息。

第 1 步。运行 设备的 DeviceClientStreamingSample 程序。你应该使用Transport.Amqp。请注意,其他人在我的测试中失败了。

步骤 2. 使用 REST 客户端工具生成 POST 请求。以下url是我的测试:

https://xxxxxxxxxxx.azure-devices.net/twins/device1/streams/teststream?api-version=2018-08-30-preview

headers:

accept: application/json
iothub-streaming-response-timeout-in-seconds: 15
iothub-streaming-connect-timeout-in-seconds: 5
Authorization: sas-token

有效负载: 任何或空

以下屏幕片段展示了 POST 已发送到设备 1:

并且以下代码片段显示了在 WaitForDeviceStreamRequestAsync 方法之后的断点处停止程序:

设备和服务(在本例中是我的测试仪)之间的完整握手和流式传输显示在以下屏幕片段中:

正如我提到的,Azure IoT 中心测试器已经实现了设备流功能,以下屏幕片段显示了流缓冲区:

注意,运行TransportType.Mqtt的模拟设备不工作,症状和你一样,超时。看起来(基于 REST POST 响应),为 $iothub/streams/POST/#[=23 等主题订阅设备时存在错误=]

但是,当您的设备使用直接 MQTT 协议连接到 IoT 中心时,一切正常,请参阅我的测试仪的屏幕片段,其中 device1 连接到物联网中心:

屏幕上出现 Azure IoT 中心测试器后,您可以使用它来测试用于流式传输的服务 SDK,例如 SDK 示例中的以下行:

DeviceStreamResponse result = await _serviceClient.CreateStreamAsync(_deviceId, deviceStreamRequest).ConfigureAwait(false);

测试仪将显示 device1 流:

总之,根据我的上述测试,当模拟设备配置为 TransportType.Amqp 协议时,您应该可以成功使用 SDK 进行流式传输。

更新:

在使用 REST 客户端工具的情况下,您可以在 headers:

中看到设备握手的响应

此响应由以下行生成:

await _deviceClient.AcceptDeviceStreamRequestAsync(streamRequest, cancellationTokenSource.Token).ConfigureAwait(false);

基于响应 headers 例如

iothub-streaming-is-accepted
iothub-streaming-url
iothub-streaming-auth-token

可以通过 IoT Hub 在设备和服务之间建立 webSocket 流式通信。 注意,此点之后,使用REST客户端工具模拟设备会失败。

问题出在我使用的 DeviceClient 实例中,它是根据来自配置服务的信息构建的:

ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(Constants.IoTProvisioningService.GlobalDeviceEndpoint, Constants.IoTProvisioningService.IdScope, security, new ProvisioningTransportHandlerHttp());

var result = await provClient.RegisterAsync();

if (result.Status != ProvisioningRegistrationStatusType.Assigned)
    return;

var auth = new DeviceAuthenticationWithTpm(result.DeviceId, security);

--> DeviceClient deviceClient = DeviceClient.Create(result.AssignedHub, auth, TransportType.Amqp);

然后我正在使用 DeviceClient 实例来等待请求:

var buffer = new byte[1024];

using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));

--> DeviceStreamRequest streamRequest = await deviceClient.WaitForDeviceStreamRequestAsync(cancellationTokenSource.Token);

但是没有用。

我意识到 in the samples (DeviceClientStreamingSample) 连接字符串必须包含我没有的设备 ID:

// String containing Hostname, Device Id & Device Key in one of the following formats:
//  "HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"
//  "HostName=<iothub_host_name>;CredentialType=SharedAccessSignature;DeviceId=<device_id>;SharedAccessSignature=SharedAccessSignature sr=<iot_host>/devices/<device_id>&sig=<token>&se=<expiry_time>";

// For this sample either
// - pass this value as a command-prompt argument
// - set the IOTHUB_DEVICE_CONN_STRING environment variable 
// - create a launchSettings.json (see launchSettings.json.template) containing the variable
private static string s_deviceConnectionString = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_CONN_STRING");

所以我停止重复使用从配置服务获得的 DeviceClient 实例并构建了一个新实例(代码尚未准备好生产 - 这是一个原型):

var buffer = new byte[1024];

using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));

var deviceClient = DeviceClient.CreateFromConnectionString(Constants.IoTHub.ConnectionString + $";DeviceId={Constants.Unit.SerialNumber}", TransportType.Amqp);

DeviceStreamRequest streamRequest = await deviceClient.WaitForDeviceStreamRequestAsync(cancellationTokenSource.Token).ConfigureAwait(false);

if (streamRequest is null)
    return;

await deviceClient.AcceptDeviceStreamRequestAsync(streamRequest, cancellationTokenSource.Token);