Usage example
This topic uses the ./demos/mqtt_x509_auth_demo.c file as an example to demonstrate how to use an X.509 certificate to connect a device over MQTT.
Background information
For more information about connecting a device using an X.509 certificate, see Overview.
- Except for the feature configurations described in Step 2: Configure features, the steps in this topic are the same as those in Connect to MQTT.
Step 1: Initialization
Add header files.
#include "aiot_state_api.h" #include "aiot_sysdep_api.h" #include "aiot_mqtt_api.h"Configure underlying dependencies and log output.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);Call aiot_mqtt_init to create an MQTT client instance and initialize default parameters.
mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; }
Step 2: Configure features
Call aiot_mqtt_setopt to configure the following features.
- Configure connection parameters.
- Configure a callback for device identity information.
- Configure status monitoring and message callbacks.
For more configuration items, see aiot_mqtt_option_t.
- Configure connection parameters.
const char client_cert[] = { "-----BEGIN CERTIFICATE-----\r\n" "MIIDiDCCAnCgAwIBAgIIAJ3GD7c2860wDQYJKoZIhvcNAQELBQAwUzEoMCYGA1UE\r\n" … … "v4aDacYavCH03JXKQ6zWpAwnwLcYrbW7XdhtDrqFCj+v6VJ6NDZaTGEW3/I=\r\n" "-----END CERTIFICATE-----\r\n" }; const char client_private_key[] = { "-----BEGIN RSA PRIVATE KEY-----\r\n" "MIIEowIBAAKCAQEApyRaelm4b4sKOlqBywOIR4RIJrYEfNtYIAofMIkkwnClrqgh\r\n" … … "mPw5JEAkNBy6wOWepJ9Tv1wY8yFEzV2dVsx3P93p5P3UdZb4M7i0\r\n" "-----END RSA PRIVATE KEY-----\r\n" }; ... int main(int argc, char *argv[]) { int32_t res = STATE_SUCCESS; void *mqtt_handle = NULL; char *host = "x509.itls.cn-shanghai.aliyuncs.com"; uint16_t port = 1883; aiot_sysdep_network_cred_t cred; char *product_key = ""; char *device_name = ""; char *device_secret = ""; ... /* Security credential struct. If you use TLS, configure parameters such as the CA certificate in this struct. */ aiot_sysdep_network_cred_t cred; /* Create security credentials for the SDK to establish a TLS connection. */ memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t)); cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* Use an RSA certificate to authenticate the MQTT server. */ cred.max_tls_fragment = 16384; /* The maximum fragment length is 16 KB. Other valid values are 4 KB, 2 KB, 1 KB, and 0.5 KB. */ cred.sni_enabled = 1; /* Enable Server Name Indication (SNI) for TLS connections. */ cred.x509_server_cert = ali_ca_crt; /* The RSA root certificate that is used to authenticate the MQTT server. */ cred.x509_server_cert_len = strlen(ali_ca_crt); /* The length of the RSA root certificate that is used to authenticate the MQTT server. */ /* TODO: Note the following four lines of code. When you use X.509 mutual authentication, you only need to add this part to configure the security credentials. */ cred.x509_client_cert = client_cert; cred.x509_client_cert_len = strlen(client_cert); cred.x509_client_privkey = client_private_key; cred.x509_client_privkey_len = strlen(client_private_key); /* Configure the security credentials for the network connection. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred); ... }Parameter Example Description client_cert[] "-----BEGIN CERTIFICATE-----\r\n" "MIIDiDCCAnCgAwIBAgIIAJ3GD7c2860wDQYJKoZIhvcNAQELBQAwUzEoMCYGA1UE\r\n" … … "v4aDacYavCH03JXKQ6zWpAwnwLcYrbW7XdhtDrqFCj+v6VJ6NDZaTGEW3/I=\r\n" "-----END CERTIFICATE-----\r\n"The X.509 certificate of the device. On the device details page in the IoT Platform console, click Download next to X.509 Certificate to download the certificate information. After you decompress the certificate file, replace the value of this parameter with the information in the
.cerfile. Use the format shown in the example.The certificate content consists of multiple lines of strings. The ellipsis (...) in the example indicates that some strings are omitted. Add a quotation mark (
") at the beginning of each line and add\r\n"at the end of each line.client_private_key[] "-----BEGIN RSA PRIVATE KEY-----\r\n" "MIIEowIBAAKCAQEApyRaelm4b4sKOlqBywOIR4RIJrYEfNtYIAofMIkkwnClrqgh\r\n" … … "mPw5JEAkNBy6wOWepJ9Tv1wY8yFEzV2dVsx3P93p5P3UdZb4M7i0\r\n" "-----END RSA PRIVATE KEY-----\r\n"The X.509 certificate key of the device. On the device details page in the IoT Platform console, click Download next to X.509 Certificate to download the certificate information. After you decompress the certificate file, replace the value of this parameter with the information in the
.keyfile. Use the format shown in the example.The certificate key content consists of multiple lines of strings. The ellipsis (...) in the example indicates that some strings are omitted. Add a quotation mark (
") at the beginning of each line and add\r\n"at the end of each line.host x509.itls.cn-shanghai.aliyuncs.com The format is x509.itls.${YourRegionId}.aliyuncs.com.In the format, ${YourRegionId} is the ID of the region where the device is connected. For more information, see Regions and zones.
port 1883 The port number. product_key "" You do not need to configure these three parameters for X.509 authentication. Make sure their values are empty. device_name "" device_secret ""
- Configure a callback for device identity information.After a device connects to IoT Platform using an X.509 certificate, IoT Platform sends a message to the device that contains its ProductKey and DeviceName. You must configure a callback function to receive this message. Use the callback function to save these two parameters to a specified location for later use.
You need to create a user-defined function named
demo_get_device_info. The following sample code shows how to parse and print the data in the message.static void demo_get_device_info(const char *topic, uint16_t topic_len, const char *payload, uint32_t payload_len) { const char *target_topic = "/ext/auth/identity/response"; char *p_product_key = NULL; uint32_t product_key_len = 0; char *p_device_name = NULL; uint32_t device_name_len = 0; int32_t res = STATE_SUCCESS; if (topic_len != strlen(target_topic) || memcmp(topic, target_topic, topic_len) != 0) { return; } /* TODO: For demonstration purposes, the internal SDK interface core_json_value() is used here. This interface is for demonstration only. In actual use, replace it with an interface from a JSON parsing function library available on the device, such as cJSON, to process the payload. */ res = core_json_value(payload, payload_len, "productKey", strlen("productKey"), &p_product_key, &product_key_len); if (res < 0) { return; } res = core_json_value(payload, payload_len, "deviceName", strlen("deviceName"), &p_device_name, &device_name_len); if (res < 0) { return; } if (g_product_key == NULL) { g_product_key = malloc(product_key_len + 1); if (NULL == g_product_key) { return; } memset(g_product_key, 0, product_key_len + 1); memcpy(g_product_key, p_product_key, product_key_len); } if (g_device_name == NULL) { g_device_name = malloc(device_name_len + 1); if (NULL == g_product_key) { return; } memset(g_device_name, 0, device_name_len + 1); memcpy(g_device_name, p_device_name, device_name_len); } printf("device productKey: %s\r\n", g_product_key); printf("device deviceName: %s\r\n", g_device_name); }- Related information:
IoT Platform sends the device's ProductKey and DeviceName to the topic
/ext/auth/identity/response. ThePayloadformat is as follows:{ "productKey":"***", "deviceName":"***" }
Configure status monitoring and message callbacks.
You can configure the status monitoring callback function.
Sample code:
int main(int argc, char *argv[]) { ... ... /* Configure the default MQTT message callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler); /* Configure the MQTT event callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler); ... ... }Related parameters:
Configuration item
Sample value
Description
AIOT_MQTTOPT_RECV_HANDLER
demo_mqtt_default_recv_handler
When a message is received, the corresponding processing logic defined in this callback function is executed.
AIOT_MQTTOPT_EVENT_HANDLER
demo_mqtt_event_handler
When the device connection status changes, the corresponding processing logic defined in this callback function is executed.
Define the status monitoring callback function.
ImportantDo not define time-consuming event handling logic because it can block the message receiving thread.
Connection status can change due to events such as network exceptions, successful automatic reconnections, and disconnections.
You can modify the code at the
TODOmarker to handle connection status changes.
/* This is an MQTT event callback function. This function is triggered when the device connects to, reconnects to, or disconnects from the network. For event definitions, see core/aiot_mqtt_api.h. */ void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata) { switch (event->type) { /* The aiot_mqtt_connect() function is called to establish a connection with the MQTT server. */ case AIOT_MQTTEVT_CONNECT: { printf("AIOT_MQTTEVT_CONNECT\n"); /* TODO: Handle the successful connection establishment of the SDK. Do not call long-running blocking functions here. */ } break; /* The SDK is passively disconnected due to network issues and then automatically and successfully reconnects. */ case AIOT_MQTTEVT_RECONNECT: { printf("AIOT_MQTTEVT_RECONNECT\n"); /* TODO: Handle the successful reconnection of the SDK. Do not call long-running blocking functions here. */ } break; /* The SDK is passively disconnected due to network issues. This can be caused by a read/write failure at the network layer or a failure to receive a heartbeat response from the server as expected. */ case AIOT_MQTTEVT_DISCONNECT: { char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") : ("heartbeat disconnect"); printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause); /* TODO: Handle the passive disconnection of the SDK. Do not call long-running blocking functions here. */ } break; default: { } } }Define the message-receiving callback function.
ImportantAvoid defining time-consuming event handling logic to prevent blocking the message-receiving thread.
To handle received messages, modify the code at the
TODOmarker.
/* This is the default MQTT message callback function. This function is called when the SDK receives an MQTT message from the server and you have not configured a specific callback for it. */ void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata) { switch (packet->type) { case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: { printf("heartbeat response\n"); /* TODO: Handle the server's response to the heartbeat. This is generally not required. */ } break; case AIOT_MQTTRECV_SUB_ACK: { printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n", -packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos); /* TODO: Handle the server's response to the subscription request. This is generally not required. */ } break; case AIOT_MQTTRECV_PUB: { printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic); printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload); /* TODO: Handle the service message sent from the server. */ } break; case AIOT_MQTTRECV_PUB_ACK: { printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id); /* TODO: Handle the server's response to a reported message with QoS=1. This is generally not required. */ } break; default: { } } }
Step 3: Request a connection
Call aiot_mqtt_connect to send a connection authentication request to IoT Platform.
/* Establish an MQTT connection with the server. */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* If the connection fails, destroy the MQTT instance to release resources. */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n", -res);
return -1;
}Step 4: Start the keepalive thread
Call the aiot_mqtt_process function to send heartbeat messages to the server. This function maintains a persistent connection for the device and resends unacknowledged messages that have a Quality of Service (QoS) level of 1.
Start the keepalive thread.
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_process_thread failed: %d\n", res); return -1; }Set the keepalive thread handler function.
void *demo_mqtt_process_thread(void *args) { int32_t res = STATE_SUCCESS; while (g_mqtt_process_thread_running) { res = aiot_mqtt_process(args); if (res == STATE_USER_INPUT_EXEC_DISABLED) { break; } sleep(1); } return NULL; }
Step 5: Start the receiving thread
Call aiot_mqtt_recv to receive MQTT messages from the server. The message callback function processes these messages. If the device is disconnected, it automatically reconnects and triggers the event callback function.
Start the receiving thread.
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle); if (res < 0) { printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res); return -1; }Set the receiving thread handler function.
void *demo_mqtt_recv_thread(void *args) { int32_t res = STATE_SUCCESS; while (g_mqtt_recv_thread_running) { res = aiot_mqtt_recv(args); if (res < STATE_SUCCESS) { if (res == STATE_USER_INPUT_EXEC_DISABLED) { break; } sleep(1); } } return NULL; }
Step 6: Subscribe to a topic
Call the aiot_mqtt_sub operation to subscribe to a specific topic.
Sample code:
{ char *sub_topic = "/a18wP******/LightSwitch/user/get"; res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }NoteAfter you complete the configuration, delete the comment symbols from the relevant code.
Related parameters:
Parameter
Example
Description
sub_topic
/a18wP******/LightSwitch/user/get
A topic that the device has permission to subscribe to.
a18wP******is the ProductKey of the device.LightSwitchis the DeviceName of the device.
This example uses a default custom topic.
The device receives messages from IoT Platform through this topic.
For more information about topics, see What is a topic?.
Step 7: Send a message
Call aiot_mqtt_pub to send a message to a specific topic.
Sample code:
{ char *pub_topic = "/a18wP******/LightSwitch/user/update"; char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }NoteAfter you complete the configuration, delete the comment symbols around the relevant code.
Related parameters:
Parameter
Example
Description
pub_topic
/a18wP******/LightSwitch/user/update
A topic that the device has permission to publish to.
a18wP******is the ProductKey of the device.LightSwitchis the DeviceName of the device.
The device sends messages to IoT Platform through this topic.
For more information about topics, see What is a topic?.
pub_payload
{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}
The content of the message reported to IoT Platform.
Because the topic category for the sample message is custom, the data format can also be custom.
For more information about data formats, see Data formats.
After the device establishes MQTT communication with IoT Platform, ensure that the communication volume does not exceed the threshold.
For more information about communication limits, see Limits.
If the communication volume exceeds the threshold, log on to the IoT Platform console to view backlogged messages. For more information, see View and monitor consumer groups.
Step 8: Disconnect
MQTT is typically used for devices that require a persistent connection. Therefore, the program usually does not reach this point.
In the sample program, the main thread is responsible for configuring parameters and establishing a connection. After the connection is established, the main thread can enter hibernation.
You can call aiot_mqtt_disconnect to send a disconnection message to IoT Platform and disconnect from the network.
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}Step 9: Exit the program
You can call aiot_mqtt_deinit to destroy the MQTT client instance and release resources.
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}What to do next
./output/mqtt-x509-auth-demo.For more information, see Compile and run.
View the operational logs.