适用于 C 语言的 Azure IoT 设备 SDK - 有关 IoTHubClient 的详细信息Azure IoT device SDK for C – more about IoTHubClient

适用于 C 语言的 Azure IoT 设备 SDK 是本系列中的第一篇文章,介绍了适用于 C 语言的 Azure IoT 设备 SDK。该文章已说明 SDK 中有两个体系结构层。Azure IoT device SDK for C is the first article in this series introducing the Azure IoT device SDK for C. That article explained that there are two architectural layers in SDK. 底层是 IoTHubClient 库,用于直接管理与 IoT 中心的通信。At the base is the IoTHubClient library that directly manages communication with IoT Hub. 另有一个 序列化程序 库,它构建在 SDK 的顶部,可提供序列化服务。There's also the serializer library that builds on top of that to provide serialization services. 在本文中,我们将提供有关 IoTHubClient 库的更多详细信息。In this article, we'll provide additional detail on the IoTHubClient library.

备注

本文中提到的某些功能(例如云到设备消息传递、设备孪生、设备管理)仅在 IoT 中心的标准层中提供。Some of the features mentioned in this article, like cloud-to-device messaging, device twins, and device management, are only available in the standard tier of IoT hub. 有关基本和标准 IoT 中心层的详细信息,请参阅如何选择合适的 IoT 中心层For more information about the basic and standard IoT Hub tiers, see How to choose the right IoT Hub tier.

前一篇文章介绍了如何使用 IoTHubClient 库将事件发送到 IoT 中心及接收消息。The previous article described how to use the IoTHubClient library to send events to IoT Hub and receive messages. 本文扩大讨论范围,介绍 较低级别 API ,介绍如何更精确地管理发送和接收数据的 时机This article extends that discussion by explaining how to more precisely manage when you send and receive data, introducing you to the lower-level APIs. 此外将说明如何使用 IoTHubClient 库中的属性处理功能,将属性附加到事件(以及从消息中检索属性)。We'll also explain how to attach properties to events (and retrieve them from messages) using the property handling features in the IoTHubClient library. 最后,进一步说明如何以不同的方式来处理从 IoT 中心收到的消息。Finally, we'll provide additional explanation of different ways to handle messages received from IoT Hub.

本文结尾提供了几个其他主题,深入介绍了设备凭据以及如何通过配置选项更改 IoTHubClient 的行为。The article concludes by covering a couple of miscellaneous topics, including more about device credentials and how to change the behavior of the IoTHubClient through configuration options.

我们将使用 IoTHubClient SDK 示例来阐释这些主题。We'll use the IoTHubClient SDK samples to explain these topics. 如果想要继续,请参阅适用于 C 的 Azure IoT 设备 SDK 中随附的 iothub_client_sample_http and iothub_client_sample_amqp 应用程序。以下部分所述的所有内容都将通过这些示例来演示。If you want to follow along, see the iothub_client_sample_http and iothub_client_sample_amqp applications that are included in the Azure IoT device SDK for C. Everything described in the following sections is demonstrated in these samples.

可在 GitHub 存储库中找到适用于 C 语言的 Azure IoT 设备 SDK,还可在 C API 参考中查看 API 的详细信息。You can find the Azure IoT device SDK for C GitHub repository and view details of the API in the C API reference.

较低级别 APIThe lower-level APIs

前一篇文章介绍了 IotHubClient 应用程序上下文中 iothub_client_sample_amqp 的基本操作。The previous article described the basic operation of the IotHubClient within the context of the iothub_client_sample_amqp application. 例如,该文章说明了如何使用此代码来初始化库。For example, it explained how to initialize the library using this code.

IOTHUB_CLIENT_HANDLE iotHubClientHandle;
iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, AMQP_Protocol);

此外,还说明了如何使用此函数调用来发送事件。It also described how to send events using this function call.

IoTHubClient_SendEventAsync(iotHubClientHandle, message.messageHandle, SendConfirmationCallback, &message);

该文章还说明了如何通过注册回调函数来接收消息。The article also described how to receive messages by registering a callback function.

int receiveContext = 0;
IoTHubClient_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext);

该文章同时演示了如何使用如下代码释放资源。The article also showed how to free resources using code such as the following.

IoTHubClient_Destroy(iotHubClientHandle);

其中每个 API 都有伴随函数:There are companion functions for each of these APIs:

  • IoTHubClient_LL_CreateFromConnectionStringIoTHubClient_LL_CreateFromConnectionString
  • IoTHubClient_LL_SendEventAsyncIoTHubClient_LL_SendEventAsync
  • IoTHubClient_LL_SetMessageCallbackIoTHubClient_LL_SetMessageCallback
  • IoTHubClient_LL_DestroyIoTHubClient_LL_Destroy

这些函数的 API 名称中都包含“LL” 。These functions all include LL in the API name. 除了名称的 LL 部分之外,其中每个函数的参数都与其非 LL 的对应项相同。Other the LL part of the name, the parameters of each of these functions are identical to their non-LL counterparts. 但是,这些函数的行为有一个重要的差异。However, the behavior of these functions is different in one important way.

当你调用 IoTHubClient_CreateFromConnectionString 时,基础库将创建在后台运行的新线程。When you call IoTHubClient_CreateFromConnectionString, the underlying libraries create a new thread that runs in the background. 此线程将事件发送到 IoT 中心以及从 IoT 中心接收消息。This thread sends events to, and receives messages from, IoT Hub. 使用 LL API 时不会创建此类线程。No such thread is created when working with the LL APIs. 创建后台线程是为了给开发人员提供方便。The creation of the background thread is a convenience to the developer. 你无需担心如何明确与 IoT 中心相互发送和接收消息 -- 此操作会在后台自动进行。You don’t have to worry about explicitly sending events and receiving messages from IoT Hub -- it happens automatically in the background. 相比之下,LL API 可让你根据需要显式控制与 IoT 中心的通信。In contrast, the LL APIs give you explicit control over communication with IoT Hub, if you need it.

为了更好地理解此概念,让我们看一个示例:To understand this concept better, let’s look at an example:

当调用 IoTHubClient_SendEventAsync 时,其实是将事件放入缓冲区中。When you call IoTHubClient_SendEventAsync, what you're actually doing is putting the event in a buffer. 调用 IoTHubClient_CreateFromConnectionString 时创建的后台线程将持续监视此缓冲区,并将它包含的任何数据发送到 IoT 中心。The background thread created when you call IoTHubClient_CreateFromConnectionString continually monitors this buffer and sends any data that it contains to IoT Hub. 这些操作在主线程执行其他工作的同时在后台进行。This happens in the background at the same time that the main thread is performing other work.

同样,当使用 IoTHubClient_SetMessageCallback 注册消息的回调函数时,则会指示 SDK 在收到消息时让后台线程调用回调函数(独立于主线程)。Similarly, when you register a callback function for messages using IoTHubClient_SetMessageCallback, you're instructing the SDK to have the background thread invoke the callback function when a message is received, independent of the main thread.

LL API 不会创建后台线程。The LL APIs don’t create a background thread. 必须调用一个新 API 明确地与 IoT 中心之间相互发送和接收数据。Instead, a new API must be called to explicitly send and receive data from IoT Hub. 以下示例就将此进行演示。This is demonstrated in the following example.

SDK 中附带的 iothub_client_sample_http 应用程序演示了较低级别的 API。The iothub_client_sample_http application that’s included in the SDK demonstrates the lower-level APIs. 在该示例中,使用如下代码将事件发送到 IoT 中心:In that sample, we send events to IoT Hub with code such as the following:

EVENT_INSTANCE message;
sprintf_s(msgText, sizeof(msgText), "Message_%d_From_IoTHubClient_LL_Over_HTTP", i);
message.messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText));

IoTHubClient_LL_SendEventAsync(iotHubClientHandle, message.messageHandle, SendConfirmationCallback, &message)

前三行创建消息,最后一行发送事件。The first three lines create the message, and the last line sends the event. 但是,如前所述,发送事件意味着数据只是放在缓冲区中。However, as mentioned previously, sending the event means that the data is simply placed in a buffer. 调用 IoTHubClient_LL_SendEventAsync 时,不会在网络上传输任何内容。Nothing is transmitted on the network when we call IoTHubClient_LL_SendEventAsync. 若要实际将数据引入 IoT 中心,必须调用 IoTHubClient_LL_DoWork,如以下示例所示:In order to actually ingress the data to IoT Hub, you must call IoTHubClient_LL_DoWork, as in this example:

while (1)
{
    IoTHubClient_LL_DoWork(iotHubClientHandle);
    ThreadAPI_Sleep(100);
}

此代码(来自 iothub_client_sample_http 应用程序)反复调用 IoTHubClient_LL_DoWorkThis code (from the iothub_client_sample_http application) repeatedly calls IoTHubClient_LL_DoWork. 每次 IoTHubClient_LL_DoWork 被调用时,它会将某些事件从缓冲区发送到 IoT 中心,并检索正在发送到设备的排队消息。Each time IoTHubClient_LL_DoWork is called, it sends some events from the buffer to IoT Hub and it retrieves a queued message being sent to the device. 对于后一种情况,意味着如果已注册消息的回调函数,则调用回调(假设所有消息都已加入队列)。The latter case means that if we registered a callback function for messages, then the callback is invoked (assuming any messages are queued up). 使用如下代码来注册此类回调函数:We would have registered such a callback function with code such as the following:

IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext)

经常在循环中调用 IoTHubClient_LL_DoWork 的原因是每次调用它时,它都会将一些缓冲的事件发送到 IoT 中心,并检索设备的下一个排队消息。The reason that IoTHubClient_LL_DoWork is often called in a loop is that each time it’s called, it sends some buffered events to IoT Hub and retrieves the next message queued up for the device. 每次调用并不保证发送所有缓冲的事件或检索所有排队的消息。Each call isn’t guaranteed to send all buffered events or to retrieve all queued messages. 如果想要发送缓冲区中的所有事件,并继续进行其他处理,可以使用如下代码来替换此循环:If you want to send all events in the buffer and then continue on with other processing you can replace this loop with code such as the following:

IOTHUB_CLIENT_STATUS status;

while ((IoTHubClient_LL_GetSendStatus(iotHubClientHandle, &status) == IOTHUB_CLIENT_OK) && (status == IOTHUB_CLIENT_SEND_STATUS_BUSY))
{
    IoTHubClient_LL_DoWork(iotHubClientHandle);
    ThreadAPI_Sleep(100);
}

此代码将一直调用 IoTHubClient_LL_DoWork,直到缓冲区中的所有事件都发送到 IoT 中心为止。This code calls IoTHubClient_LL_DoWork until all events in the buffer have been sent to IoT Hub. 请注意,这并不表示收到所有已排队的消息。Note this does not also imply that all queued messages have been received. 部分原因是检查“所有”消息的确定性并不如操作那样强。Part of the reason for this is that checking for "all" messages isn’t as deterministic an action. 如果检索了“所有”消息,但随即将另一个消息发送到设备,会发生什么情况?What happens if you retrieve "all" of the messages, but then another one is sent to the device immediately after? 更好的处理方法是使用设定的超时。A better way to deal with that is with a programmed timeout. 例如,每次调用消息回调函数时,可以重置计时器。For example, the message callback function could reset a timer every time it’s invoked. 例如,如果在过去 X 秒内未收到任何消息,可以接着编写逻辑来继续处理。You can then write logic to continue processing if, for example, no messages have been received in the last X seconds.

完成引入事件和接收消息时,请务必调用相应的函数来清理资源。When you’re finished ingressing events and receiving messages, be sure to call the corresponding function to clean up resources.

IoTHubClient_LL_Destroy(iotHubClientHandle);

基本上,只有一组 API 使用后台线程来发送和接收数据,而另一组 API 不会使用后台线程来执行相同的操作。Basically there’s only one set of APIs to send and receive data with a background thread and another set of APIs that does the same thing without the background thread. 许多开发人员可能偏好非 LL API,但是当他们想要明确控制网络传输时,较低级别 API 就很有用。A lot of developers may prefer the non-LL APIs, but the lower-level APIs are useful when the developer wants explicit control over network transmissions. 例如,有些设备会收集各时间段的数据,并且只按指定的时间间隔引入事件(例如,每小时一次或每天一次)。For example, some devices collect data over time and only ingress events at specified intervals (for example, once an hour or once a day). 较低级别 API 可以在与 IoT 中心之间发送和接收数据时提供明确控制的能力。The lower-level APIs give you the ability to explicitly control when you send and receive data from IoT Hub. 还有一些人纯粹偏好较低级别 API 提供的简单性。Others will simply prefer the simplicity that the lower-level APIs provide. 所有操作都发生在主线程上,而不是有些操作在后台发生。Everything happens on the main thread rather than some work happening in the background.

无论选择哪种模型,都必须与使用的 API 相一致。Whichever model you choose, be sure to be consistent in which APIs you use. 如果首先调用 IoTHubClient_LL_CreateFromConnectionString,则对于任何后续工作,请务必只使用相应的较低级别的 API:If you start by calling IoTHubClient_LL_CreateFromConnectionString, be sure you only use the corresponding lower-level APIs for any follow-up work:

  • IoTHubClient_LL_SendEventAsyncIoTHubClient_LL_SendEventAsync

  • IoTHubClient_LL_SetMessageCallbackIoTHubClient_LL_SetMessageCallback

  • IoTHubClient_LL_DestroyIoTHubClient_LL_Destroy

  • IoTHubClient_LL_DoWorkIoTHubClient_LL_DoWork

相反的情况也成立。The opposite is true as well. 如果首先调用 IoTHubClient_CreateFromConnectionString,请使用非 LL API 进行任何其他处理。If you start with IoTHubClient_CreateFromConnectionString, then use the non-LL APIs for any additional processing.

在适用于 C 语言的 Azure IoT 设备 SDK 中,查看 iothub_client_sample_http 应用程序是否有较低级别 API 的完整示例。In the Azure IoT device SDK for C, see the iothub_client_sample_http application for a complete example of the lower-level APIs. 有关非 LL API 的完整示例,请参考 iothub_client_sample_amqp 应用程序。The iothub_client_sample_amqp application can be referenced for a full example of the non-LL APIs.

属性处理Property handling

在介绍发送数据时,我们多次提到了消息正文。So far when we've described sending data, we've been referring to the body of the message. 例如,假设有以下代码:For example, consider this code:

EVENT_INSTANCE message;
sprintf_s(msgText, sizeof(msgText), "Hello World");
message.messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText));
IoTHubClient_LL_SendEventAsync(iotHubClientHandle, message.messageHandle, SendConfirmationCallback, &message)

此示例将包含文本“Hello World”的消息发送到 IoT 中心。This example sends a message to IoT Hub with the text "Hello World." 但是,IoT 中心也允许将属性附加到每个消息。However, IoT Hub also allows properties to be attached to each message. 这些属性是可附加到消息的名称/值对。Properties are name/value pairs that can be attached to the message. 例如,我们可以修改上述代码,以将属性附加到消息:For example, we can modify the previous code to attach a property to the message:

MAP_HANDLE propMap = IoTHubMessage_Properties(message.messageHandle);
sprintf_s(propText, sizeof(propText), "%d", i);
Map_AddOrUpdate(propMap, "SequenceNumber", propText);

首先调用 IoTHubMessage_Properties,然后将消息的句柄传递给它。We start by calling IoTHubMessage_Properties and passing it the handle of our message. 返回的结果是 MAP_HANDLE 引用,这使得我们可以开始添加属性。What we get back is a MAP_HANDLE reference that enables us to start adding properties. 后一项操作是通过调用 Map_AddOrUpdate(使用对 MAP_HANDLE、属性名称和属性值的引用)来实现的。The latter is accomplished by calling Map_AddOrUpdate, which takes a reference to a MAP_HANDLE, the property name, and the property value. 使用此 API 可需要任意数目的属性。With this API we can add as many properties as we like.

事件中心读取事件时,接收方可以枚举属性并检索其对应值。When the event is read from Event Hubs, the receiver can enumerate the properties and retrieve their corresponding values. 例如,在 .NET 中,这可以通过访问 EventData 对象中的属性集合来实现。For example, in .NET this would be accomplished by accessing the Properties collection on the EventData object.

在上述示例中,我们已将属性附加到发送给 IoT 中心的事件。In the previous example, we’re attaching properties to an event that we send to IoT Hub. 属性也可以附加到从 IoT 中心接收的消息。Properties can also be attached to messages received from IoT Hub. 如果想要从消息检索属性,可以在消息回调函数中使用如下所示的代码:If we want to retrieve properties from a message, we can use code such as the following in our message callback function:

static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
    . . .

    // Retrieve properties from the message
    MAP_HANDLE mapProperties = IoTHubMessage_Properties(message);
    if (mapProperties != NULL)
    {
        const char*const* keys;
        const char*const* values;
        size_t propertyCount = 0;
        if (Map_GetInternals(mapProperties, &keys, &values, &propertyCount) == MAP_OK)
        {
            if (propertyCount > 0)
            {
                printf("Message Properties:\r\n");
                for (size_t index = 0; index < propertyCount; index++)
                {
                    printf("\tKey: %s Value: %s\r\n", keys[index], values[index]);
                }
                printf("\r\n");
            }
        }
    }

    . . .
}

调用 IoTHubMessage_Properties 会返回 MAP_HANDLE 引用。The call to IoTHubMessage_Properties returns the MAP_HANDLE reference. 然后我们将该引用传递给 Map_GetInternals,以获取对名称/值对数组的引用(以及属性的计数)。We then pass that reference to Map_GetInternals to obtain a reference to an array of the name/value pairs (as well as a count of the properties). 此时可以很轻松地枚举属性以获取所需的值。At that point it's a simple matter of enumerating the properties to get to the values we want.

无需在应用程序中使用属性。You don't have to use properties in your application. 但是,如果需要在事件中设置属性或者从消息中检索属性,使用 IoTHubClient 库很轻松就能做到。However, if you need to set them on events or retrieve them from messages, the IoTHubClient library makes it easy.

消息处理Message handling

如前所述,当 IoT 中心发出的消息抵达时, IoTHubClient 库将调用注册的回调函数来做出响应。As stated previously, when messages arrive from IoT Hub the IoTHubClient library responds by invoking a registered callback function. 有必要进一步了解此函数的一个返回参数。There is a return parameter of this function that deserves some additional explanation. 以下是 iothub_client_sample_http 示例应用程序中回调函数的摘录:Here’s an excerpt of the callback function in the iothub_client_sample_http sample application:

static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
    . . .
    return IOTHUBMESSAGE_ACCEPTED;
}

请注意,返回类型为 IOTHUBMESSAGE_DISPOSITION_RESULT,在本特定案例中,将返回 IOTHUBMESSAGE_ACCEPTEDNote that the return type is IOTHUBMESSAGE_DISPOSITION_RESULT and in this particular case we return IOTHUBMESSAGE_ACCEPTED. 此函数还可能返回其他值,这些值将改变 IoTHubClient 库响应消息回调的方式。There are other values we can return from this function that change how the IoTHubClient library reacts to the message callback. 选项如下。Here are the options.

  • IOTHUBMESSAGE_ACCEPTED - 消息已成功处理。IOTHUBMESSAGE_ACCEPTED – The message has been processed successfully. IoTHubClient 库不对同一消息再次调用回调函数。The IoTHubClient library will not invoke the callback function again with the same message.

  • IOTHUBMESSAGE_REJECTED - 未处理消息,且将来也不会处理。IOTHUBMESSAGE_REJECTED – The message was not processed and there is no desire to do so in the future. IoTHubClient 库不应该对同一消息再次调用回调函数。The IoTHubClient library should not invoke the callback function again with the same message.

  • IOTHUBMESSAGE_ABANDONED - 消息未成功处理,但 IoTHubClient 库应该对同一消息再次调用回调函数。IOTHUBMESSAGE_ABANDONED – The message was not processed successfully, but the IoTHubClient library should invoke the callback function again with the same message.

对于前两个返回代码,IoTHubClient 库会将消息发送到 IoT 中心,指示应该从设备队列中删除消息且不再传送。For the first two return codes, the IoTHubClient library sends a message to IoT Hub indicating that the message should be deleted from the device queue and not delivered again. 最终结果一样(从设备队列删除消息),但还记录了是已接受还是已拒绝消息。The net effect is the same (the message is deleted from the device queue), but whether the message was accepted or rejected is still recorded. 对于可听取反馈并了解设备是已接受还是拒绝特定消息的消息发送者而言,记录这种区分信息的功能非常有用。Recording this distinction is useful to senders of the message who can listen for feedback and find out if a device has accepted or rejected a particular message.

在最后一个案例中,消息也会发送到 IoT 中心,但指示应重新传送消息。In the last case a message is also sent to IoT Hub, but it indicates that the message should be redelivered. 如果遇到了错误但想要再次尝试处理消息,通常会放弃消息。Typically you’ll abandon a message if you encounter some error but want to try to process the message again. 相比之下,当遇到不可恢复的错误(或者只是决定你不想要处理消息)时,拒绝消息是适当的方式。In contrast, rejecting a message is appropriate when you encounter an unrecoverable error (or if you simply decide you don’t want to process the message).

在任何情况下,请留意不同的返回代码,以便能够推测 IoTHubClient 库的行为。In any case, be aware of the different return codes so that you can elicit the behavior you want from the IoTHubClient library.

替代设备凭据Alternate device credentials

如前所述,使用 IoTHubClient 库时,首先必须使用如下所示的调用来获取 IOTHUB_CLIENT_HANDLEAs explained previously, the first thing to do when working with the IoTHubClient library is to obtain a IOTHUB_CLIENT_HANDLE with a call such as the following:

IOTHUB_CLIENT_HANDLE iotHubClientHandle;
iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, AMQP_Protocol);

IoTHubClient_CreateFromConnectionString 的实参是设备连接字符串,并且是指示与 IoT 中心通信时所用协议的形参。The arguments to IoTHubClient_CreateFromConnectionString are the device connection string and a parameter that indicates the protocol we use to communicate with IoT Hub. 设备连接字符串的格式如下:The device connection string has a format that appears as follows:

HostName=IOTHUBNAME.IOTHUBSUFFIX;DeviceId=DEVICEID;SharedAccessKey=SHAREDACCESSKEY

此字符串中有四条信息:IoT 中心名称、IoT 中心后缀、设备 ID 和共享访问密钥。There are four pieces of information in this string: IoT Hub name, IoT Hub suffix, device ID, and shared access key. 当在 Azure 门户中创建 IoT 中心实例时,可以获取 IoT 中心的完全限定域名 (FQDN) - 它提供了 IoT 中心名称(FQDN 的第一个部分)和 IoT 中心后缀(FQDN 的其余部分)。You obtain the fully qualified domain name (FQDN) of an IoT hub when you create your IoT hub instance in the Azure portal — this gives you the IoT hub name (the first part of the FQDN) and the IoT hub suffix (the rest of the FQDN). 使用 IoT 中心注册设备时,可以获取设备 ID 和共享访问密钥(如前一篇文章中所述)。You get the Device ID and the Shared Access Key when you register your device with IoT Hub (as described in the previous article).

IoTHubClient_CreateFromConnectionString 提供了初始化库的方式。IoTHubClient_CreateFromConnectionString gives you one way to initialize the library. 如果需要,可以使用其中的每个参数而不是设备连接字符串来创建新的 IOTHUB_CLIENT_HANDLEIf you prefer, you can create a new IOTHUB_CLIENT_HANDLE by using these individual parameters rather than the device connection string. 使用以下代码即可实现此目的:This is achieved with the following code:

IOTHUB_CLIENT_CONFIG iotHubClientConfig;
iotHubClientConfig.iotHubName = "";
iotHubClientConfig.deviceId = "";
iotHubClientConfig.deviceKey = "";
iotHubClientConfig.iotHubSuffix = "";
iotHubClientConfig.protocol = HTTP_Protocol;
IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_LL_Create(&iotHubClientConfig);

结果将与使用 IoTHubClient_CreateFromConnectionString 相同。This accomplishes the same thing as IoTHubClient_CreateFromConnectionString.

显然,你更想要使用 IoTHubClient_CreateFromConnectionString,而不是这种更繁琐的初始化方法。It may seem obvious that you would want to use IoTHubClient_CreateFromConnectionString rather than this more verbose method of initialization. 但请记住,当在 IoT 中心注册设备时,获得的是设备 ID 和设备密钥(而不是连接字符串)。Keep in mind, however, that when you register a device in IoT Hub what you get is a device ID and device key (not a connection string). 前一篇文章中介绍的设备资源管理器 SDK 工具使用 Azure IoT 服务 SDK 中的库,通过设备 ID、设备密钥和 IoT 中心主机名创建设备连接字符串。The device explorer SDK tool introduced in the previous article uses libraries in the Azure IoT service SDK to create the device connection string from the device ID, device key, and IoT Hub host name. 因此调用 IoTHubClient_LL_Create 可能更好,因为这样可以免除生成连接字符串的步骤。So calling IoTHubClient_LL_Create may be preferable because it saves you the step of generating a connection string. 使用任何一种方法都很方便。Use whichever method is convenient.

配置选项Configuration options

到目前为止,有关 IoTHubClient 库工作方式的所有介绍内容都反映了其默认行为。So far everything described about the way the IoTHubClient library works reflects its default behavior. 但是,可以设置几个选项来更改库的工作方式。However, there are a few options that you can set to change how the library works. 此目的可以利用 IoTHubClient_LL_SetOption API 来实现。This is accomplished by leveraging the IoTHubClient_LL_SetOption API. 请看以下示例:Consider this example:

unsigned int timeout = 30000;
IoTHubClient_LL_SetOption(iotHubClientHandle, "timeout", &timeout);

有一些常用的选项:There are a couple of options that are commonly used:

  • SetBatching(布尔值)– 如果为 true,则将数据分批发送到 IoT 中心。SetBatching (bool) – If true, then data sent to IoT Hub is sent in batches. 如果为 false,则逐个发送消息。If false, then messages are sent individually. 默认值为 falseThe default is false. 支持通过 AMQP/AMQP-WS 进行批处理,以及在 D2C 消息上添加系统属性。Batching over AMQP / AMQP-WS, as well as adding system properties on D2C messages, is supported.

  • Timeout(无符号整数)– 此值以毫秒表示。Timeout (unsigned int) – This value is represented in milliseconds. 如果发送 HTTPS 请求或接收响应所花费的时间超过此时间,即表示连接超时。If sending an HTTPS request or receiving a response takes longer than this time, then the connection times out.

batching 选项非常重要。The batching option is important. 默认情况下,库将逐个引入事件(单个事件是你传递给 IoTHubClient_LL_SendEventAsync 的任何内容)。By default, the library ingresses events individually (a single event is whatever you pass to IoTHubClient_LL_SendEventAsync). 如果 batching 选项为 true,库将尽可能多地从缓冲区中收集事件(上限为 IoT 中心接受的消息大小上限)。If the batching option is true, the library collects as many events as it can from the buffer (up to the maximum message size that IoT Hub will accept). 事件批在单个 HTTPS 调用中发送到 IoT 中心(单个事件已捆绑到 JSON 数组中)。The event batch is sent to IoT Hub in a single HTTPS call (the individual events are bundled into a JSON array). 启用批处理通常可以大幅提升性能,因为网络往返时间会缩减。Enabling batching typically results in big performance gains since you’re reducing network round-trips. 不过,这也会明显减少带宽,你要在事件批中发送一组 HTTPS 标头,而不是针对每个事件发送一组标头。It also significantly reduces bandwidth since you are sending one set of HTTPS headers with an event batch rather than a set of headers for each individual event. 除非有使用其他方式的特殊理由,否则建议启用批处理。Unless you have a specific reason to do otherwise, typically you’ll want to enable batching.

后续步骤Next steps

本文详细探讨了适用于 C 语言的 Azure IoT 设备 SDK 中的 IoTHubClient 库的行为。参考这些信息可以充分了解 IoTHubClient 库的功能。This article describes in detail the behavior of the IoTHubClient library found in the Azure IoT device SDK for C. With this information, you should have a good understanding of the capabilities of the IoTHubClient library. 本系列中的第二篇文章是适用于 C 语言的 Azure IoT 设备 SDK - 序列化程序,它提供了有关序列化程序库的类似详细信息。The second article in this series is Azure IoT device SDK for C - Serializer, which provides similar detail on the serializer library.

若要详细了解如何针对 IoT 中心进行开发,请参阅 Azure IoT SDKTo learn more about developing for IoT Hub, see the Azure IoT SDKs.

若要进一步探索 IoT 中心的功能,请参阅使用 Azure IoT Edge 将 AI 部署到边缘设备To further explore the capabilities of IoT Hub, see Deploying AI to edge devices with Azure IoT Edge.