この記事では、X.509証明書を使用してMQTTベースのを接続する方法について説明します。/demos/mqtt_x509_auth_demo.cサンプルコードファイルを使用します。
背景情報
X.509証明書ベースの接続の詳細については、「概要」をご参照ください。
- ステップ2: 必要な機能の設定を除いて、このトピックの手順は例のトピックと同じです。
ステップ1: クライアントの初期化
ヘッダーファイルの追加
を含む# 「aiot_state_api.h」を含める # 「aiot_sysdep_api.h」を含む # 「aiot_mqtt_api.h」基になる依存関係を追加し、ログ出力機能を設定します。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);aiot_mqtt_init操作を呼び出してMQTTクライアントインスタンスを作成し、デフォルトパラメーターを初期化します。
mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; }
ステップ2: 機能の設定
aiot_mqtt_setopt操作を呼び出して、次の項目を設定します。
操作のパラメーターの詳細については、「aiot_mqtt_option_t」をご参照ください。
- 接続パラメータを設定します。
-
const char client_cert[] = { "----- 認証を開始 -----\r\n" "MIIDiDCCAnCgAwIBAgIIAJ3GD7c2860wDQYJKoZIhvcNAQELBQAwUzEoMCYGA1UE\r\n" ... ... "v4aDacYavCH03JXKQ6zWpAwnwLcYrbW7XdhtDrqFCj + v6VJ6NDZaTGEW3/I=\r\n" "----- END CERTIFICATE -----\r\n" }; const char client_private_key[] = { 「 ----- RSAプライベートキーを開始 -----\r\n」 "MIIEowIBAAKCAQEApyRaelm4b4sKOlqBywOIR4RIJrYEfNtYIAofMIkkwnClrqgh\r\n" ... ... "mPw5JEAkNBy6wOWepJ9Tv1wY8yFEzV2dVsx3P93p5P3UdZb4M7i0\r\n" "----- END RSAプライベートキー -----\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 = ""; ... /* セキュリティ資格情報の構造。 TLS接続を確立するには、この構造でCA証明書を指定します。 */ aiot_sysdep_network_cred_t cred; /* TLS接続を確立するためのSDKのセキュリティ資格情報を作成します。 */ memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t)); cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* RSA証明書を使用してMQTTブローカーを検証します。 */ cred.max_tls_fragment = 16384; /* フラグメントの長さは最大16 KBです。 その他のオプション値には、4 KB、2 KB、1 KB、および0.5 KBが含まれます。 */ cred.sni_enabled = 1; /* TLS接続を確立すると、サーバー名インジケーター (SNI) 拡張機能がサポートされます。 */ cred.x509_server_cert = ali_ca_crt; /* MQTTブローカーの検証に使用されるRSAルート証明書。 */ cred.x509_server_cert_len = strlen(ali_ca_crt); /* RSAルート証明書の長さ。 */ /* TODO: X.509証明書を双方向認証に使用する場合、次のコードを追加してセキュリティ資格情報を設定する必要があります。 */ 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); /* 接続のセキュリティ資格情報を設定します。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred); ... } パラメーター 例 説明 client_cert[] "----- 認証を開始 -----\r\n" "MIIDiDCCAnCgAwIBAgIIAJ3GD7c2860wDQYJKoZIhvcNAQELBQAwUzEoMCYGA1UE\r\n" ... ... "v4aDacYavCH03JXKQ6zWpAwnwLcYrbW7XdhtDrqFCj + v6VJ6NDZaTGEW3/I=\r\n" "----- END CERTIFICATE -----\r\n"デバイスのX.509証明書情報。 IoT Platformコンソールの [デバイスの詳細] ページで、X.509証明書の横にある [ダウンロード] をクリックして証明書情報をダウンロードします。 証明書ファイルを解凍した後、このパラメーターの値を
の情報に置き換えます。exampleの値の形式でファイルをします。証明書情報は、複数行の文字列で構成されます。 サンプルコードの省略記号 (…) は、省略された文字列を示します。 各文字列の先頭に
"、末尾に\r\n"を追加します。client_private_key[] "----- BEGIN RSA PRIVATE KEY -----\r\n" "MIIEowIBAAKCAQEApyRaelm4b4sKOlqBywOIR4RIJrYEfNtYIAofMIkkwnClrqgh\r\n" ... ... "mPw5JEAkNBy6wOWepJ9Tv1wY8yFEzV2dVsx3P93p5P3UdZb4M7i0\r\n" 「 ----- END RSAプライベートキー -----\r\n」X.509証明書の秘密鍵。 IoT Platformコンソールの [デバイスの詳細] ページで、X.509証明書の横にある [ダウンロード] をクリックして証明書情報をダウンロードします。 証明書ファイルを解凍した後、このパラメーターの値を
の情報に置き換えます。キーは、サンプル値の形式でファイルをします。秘密鍵は、複数行の文字列で構成されます。 サンプルコードの省略記号 (…) は、省略された文字列を示します。 各文字列の先頭に
"、末尾に\r\n"を追加します。host x509.itls.cn-shanghai.aliyuncs.com 形式: x509.itls.${YourRegionI d}.aliyuncs.com${YourRegionId} を、デバイスがIoT Platformに接続されているリージョンのIDに置き換えます。 詳細については、「リージョンとゾーン」をご参照ください。
port 1883 ポート番号 product_key "" 認証にX.509証明書を使用する場合は、これらの各パラメーターに空の文字列を指定します。 device_name "" device_secret ""
-
- コールバックを設定してデバイスID情報を取得します。 デバイスが認証にX.509証明書を使用し、IoT Platformとの接続を確立すると、IoT PlatformはProductKeyとDeviceNameを含むメッセージをデバイスに送信します。 メッセージを受信するには、コールバックを設定する必要があります。 コールバックは、これらのパラメータを後で使用するために指定された場所に保存するために呼び出されます。
demo_get_device_info関数をカスタマイズできます。 この例では、メッセージが解析され、印刷される。-
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: SDKのcore_json_value操作を例として指定します。 ペイロードを処理するには、操作をcJSONなどのJSON解析ライブラリからの操作に置き換える必要があります。 */ 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); } - 注:
IoT Platformは、
/ext/auth/identity/responseトピックを使用して、ProductKeyとDeviceNameをデバイスに発行します。ペイロード形式:{ "productKey":"***" 、 "deviceName":"***" }
-
コールバックを設定してステータスを監視し、メッセージを受信します。
コールバックを設定してステータスを監視します。
サンプルコード:
int main(int argc, char * argv[]) { ... ... /* MQTTメッセージを受信するデフォルトのコールバックを指定します。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler); /* MQTTイベントを処理するコールバックを指定します。 */ aiot_mqtt_setopt(mqtt_handle、AIOT_MQTTOPT_EVENT_HANDLER、(void *)demo_mqtt_event_handler); ... ... }パラメーター:
パラメーター
例
説明
AIOT_MQTTOPT_RECV_HANDLER
demo_mqtt_default_recv_handler
メッセージが受信されると、コールバックが呼び出されて必要な操作が実行されます。
AIOT_MQTTOPT_EVENT_HANDLER
demo_mqtt_event_handler
デバイス接続のステータスが変更されると、コールバックが呼び出されて必要な操作が実行されます。
コールバックを定義してステータスを監視します。
重要イベントを処理するための時間のかかるロジックを定義しないでください。 これにより、パケットの受信に使用されるスレッドがブロックされなくなります。
接続ステータスの変更には、ネットワーク例外、自動再接続、および切断が含まれます。
接続ステータスの変更を処理するには、ビジネス要件に基づいて
TODOセクションのコードを変更します。
/* MQTTイベントを処理するコールバック。 接続が作成、回復、または閉じられた場合、コールバックが呼び出されます。 イベント定義の詳細については、core/aiot_mqtt_api.hをご参照ください。 * / void demo_mqtt_event_handler(void * handle, const aiot_mqtt_event_t * event, void * userdata) { switch (event->type) { /* aiot_mqtt_connect操作を呼び出して、MQTTブローカーへの接続を確立します。 */ ケースAIOT_MQTTEVT_CONNECT: { printf("AIOT_MQTTEVT_CONNECT\n"); /* TODO: SDKとMQTTブローカー間の接続が確立された後、処理ロジックを定義します。 スレッドをブロックする可能性のある時間のかかる関数を呼び出さないでください。 */ } break; /* ネットワーク例外により切断エラーが発生した場合、SDKはMQTTブローカーとの再接続要求を自動的に開始します。 */ ケースAIOT_MQTTEVT_RECONNECT: { printf("AIOT_MQTTEVT_RECONNECT\n"); /* TODO: SDKとMQTTブローカーの間に新しい接続が確立された後、処理ロジックを定義します。 スレッドをブロックする可能性のある時間のかかる関数を呼び出さないでください。 */ } break; /* ネットワーク例外により切断エラーが発生しました。 基になる読み取りまたは書き込み操作が失敗します。 ハートビート応答はMQTTブローカーから取得されません。 */ ケースAIOT_MQTTEVT_DISCONNECT: { char * cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? (「ネットワーク切断」): (「ハートビート切断」); printf("AIOT_MQTTEVT_DISCONNECT: % s\n" 、原因); /* TODO: SDKがMQTTブローカーから受動的に切断された後、処理ロジックを定義します。 スレッドをブロックする可能性のある時間のかかる関数を呼び出さないでください。 */ } break; default: { } } }メッセージを受信するコールバックを定義します。
重要イベントを処理するための時間のかかるロジックを定義しないでください。 これにより、パケットの受信に使用されるスレッドがブロックされなくなります。
受信したメッセージを処理するには、ビジネス要件に基づいて
TODOセクションのコードを変更します。
/* MQTTメッセージを処理するためのデフォルトのコールバック。 SDKがMQTTブローカーからメッセージを受信し、コールバックを設定しない場合、次の操作が呼び出されます。 * / 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("ハートビート応答 \n"); /* TODO: MQTTブローカーからのハートビート応答を処理するロジックを定義します。 ほとんどの場合、ロジックは必要ありません。 */ } break; ケース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: サブスクリプションリクエストに対するMQTTブローカーの応答を処理するロジックを定義します。 ほとんどの場合、ロジックは必要ありません。 */ */ } break; ケース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: MQTTブローカーによって送信されるビジネスメッセージを処理するロジックを定義します。 */ } break; ケースAIOT_MQTTRECV_PUB_ACK: { printf("puback, packet id: % d\n", packet->data.pub_ack.packet_id); /* TODO: QoS 1メッセージに対するMQTTブローカーの応答を処理するロジックを定義します。 ほとんどの場合、ロジックは必要ありません。 */ } break; default: { } } }
ステップ3: 接続を確立する
aiot_mqtt_connect操作を呼び出して、認証および接続リクエストをIoT Platformに送信します。
/* IoT PlatformとのMQTT接続を確立します。 */ */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* MQTT接続の確立に失敗した場合、MQTTインスタンスのリソースを解放します。 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x % 04X\n", -res);
return -1;
}ステップ4: keep-aliveスレッドを有効にする
aiot_mqtt_process操作を呼び出して、ハートビートメッセージをMQTTブローカーに送信し、応答が生成されていないQoS 1メッセージを再送信します。 このようにして、永続的な接続が可能になる。
キープアライブスレッドを有効にします。
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; }キープアライブスレッドを管理する機能を設定します。
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; } 睡眠 (1); } NULLを返します。}
ステップ5: スレッドがメッセージを受信できるようにする
ブローカーからMQTTメッセージを受信するには、aiot_mqtt_recv操作を呼び出します。 必要な操作は、コールバックを使用してメッセージを受信することによって実行されます。 切断と自動再接続が発生した場合、必要な操作はコールバックを使用してイベントを処理します。
スレッドがメッセージを受信できるようにします。
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; }スレッドを管理する関数を設定します。
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; } 睡眠 (1); } } NULLを返します。}
ステップ6: トピックを購読する
aiot_mqtt_sub操作を呼び出して、指定したトピックをサブスクライブします。
サンプルコード:
{ 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; } }説明サンプルコードを設定したら、コードの両側にある注釈記号を削除します。
パラメーター:
パラメーター
例
説明
sub_topic
/a18wP ******/LightSwitch /ユーザー /取得
デバイスがサブスクライブ権限を持つトピック。
a1oGs ******はデバイスのProductKeyです。LightSwitchは、デバイスのDeviceNameです。
この例では、デフォルトのカスタムトピックが使用されます。
デバイスは、このトピックを使用してIoT Platformからメッセージを受信できます。
詳細については、「トピック」をご参照ください。
ステップ7: メッセージを送信する
aiot_mqtt_pub操作を呼び出して、指定したトピックにメッセージを送信します。
サンプルコード:
{ 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; } }説明サンプルコードを設定したら、コードの両側にある注釈記号を削除します。
パラメーター:
パラメーター
例
説明
pub_topic
/a18wP ******/LightSwitch /ユーザー /アップデート
デバイスが発行権限を持つトピック。
a1oGs ******はデバイスのProductKeyです。LightSwitchは、デバイスのDeviceNameです。
デバイスは、このトピックを使用してIoT Platformにメッセージを送信します。
詳細については、「トピック」をご参照ください。
pub_payload
{\"id\":\"1\",\"version\" \"1.0\",\"params\" :{\ "LightSwitch\":0}}
デバイスがIoT Platformに送信するメッセージの内容。
この例では、カスタムトピックが使用されています。 カスタムメッセージ形式を指定できます。
詳細については、「データ形式」をご参照ください。
デバイスとIoT Platform間のMQTT接続が確立されたら、メッセージ数がしきい値を超えないようにします。
通信制限の詳細については、「制限」をご参照ください。
メッセージ数がしきい値を超えた場合は、IoT Platformコンソールにログインして、蓄積されたメッセージを表示します。 詳細については、「コンシューマーグループの表示と監視」をご参照ください。
ステップ8: IoT Platformからデバイスを切断する
MQTT接続は、永続的に接続されたままのデバイスに適用されます。 IoT Platformからデバイスを手動で切断できます。
この例では、メインスレッドを使用してパラメータを設定し、接続を確立します。 接続が確立されたら、メインスレッドを一時停止できます。
aiot_mqtt_disconnect操作を呼び出して、デバイスをIoT Platformから切断します。
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;
}ステップ9: プログラムを終了する
aiot_mqtt_deinit操作を呼び出して、MQTTクライアントインスタンスを削除し、リソースをリリースします。
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x % 04X\n" 、-res);
return -1;
}