Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of AWS-test by
aws_iot_mqtt_client_yield.cpp
00001 /* 00002 * Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"). 00005 * You may not use this file except in compliance with the License. 00006 * A copy of the License is located at 00007 * 00008 * http://aws.amazon.com/apache2.0 00009 * 00010 * or in the "license" file accompanying this file. This file is distributed 00011 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 00012 * express or implied. See the License for the specific language governing 00013 * permissions and limitations under the License. 00014 */ 00015 00016 // Based on Eclipse Paho. 00017 /******************************************************************************* 00018 * Copyright (c) 2014 IBM Corp. 00019 * 00020 * All rights reserved. This program and the accompanying materials 00021 * are made available under the terms of the Eclipse Public License v1.0 00022 * and Eclipse Distribution License v1.0 which accompany this distribution. 00023 * 00024 * The Eclipse Public License is available at 00025 * http://www.eclipse.org/legal/epl-v10.html 00026 * and the Eclipse Distribution License is available at 00027 * http://www.eclipse.org/org/documents/edl-v10.php. 00028 * 00029 * Contributors: 00030 * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation 00031 *******************************************************************************/ 00032 00033 /** 00034 * @file aws_iot_mqtt_client_yield.c 00035 * @brief MQTT client yield API definitions 00036 */ 00037 00038 #ifdef __cplusplus 00039 extern "C" { 00040 #endif 00041 00042 #include "aws_iot_mqtt_client_common_internal.h" 00043 00044 /** 00045 * This is for the case when the aws_iot_mqtt_internal_send_packet Fails. 00046 */ 00047 static void _aws_iot_mqtt_force_client_disconnect(AWS_IoT_Client *pClient) { 00048 pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; 00049 pClient->networkStack.disconnect(&(pClient->networkStack)); 00050 pClient->networkStack.destroy(&(pClient->networkStack)); 00051 } 00052 00053 static IoT_Error_t _aws_iot_mqtt_handle_disconnect(AWS_IoT_Client *pClient) { 00054 IoT_Error_t rc; 00055 00056 FUNC_ENTRY; 00057 00058 rc = aws_iot_mqtt_disconnect(pClient); 00059 if(rc != IOT_SUCCESS) { 00060 // If the aws_iot_mqtt_internal_send_packet prevents us from sending a disconnect packet then we have to clean the stack 00061 _aws_iot_mqtt_force_client_disconnect(pClient); 00062 } 00063 00064 if(NULL != pClient->clientData.disconnectHandler) { 00065 pClient->clientData.disconnectHandler(pClient, pClient->clientData.disconnectHandlerData); 00066 } 00067 00068 /* Reset to 0 since this was not a manual disconnect */ 00069 pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; 00070 FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); 00071 } 00072 00073 00074 static IoT_Error_t _aws_iot_mqtt_handle_reconnect(AWS_IoT_Client *pClient) { 00075 IoT_Error_t rc; 00076 00077 FUNC_ENTRY; 00078 00079 if(!has_timer_expired(&(pClient->reconnectDelayTimer))) { 00080 /* Timer has not expired. Not time to attempt reconnect yet. 00081 * Return attempting reconnect */ 00082 FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); 00083 } 00084 00085 rc = NETWORK_PHYSICAL_LAYER_DISCONNECTED; 00086 if(NULL != pClient->networkStack.isConnected) { 00087 rc = pClient->networkStack.isConnected(&(pClient->networkStack)); 00088 } 00089 00090 if(NETWORK_PHYSICAL_LAYER_CONNECTED == rc) { 00091 rc = aws_iot_mqtt_attempt_reconnect(pClient); 00092 if(NETWORK_RECONNECTED == rc) { 00093 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, 00094 CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); 00095 if(IOT_SUCCESS != rc) { 00096 FUNC_EXIT_RC(rc); 00097 } 00098 FUNC_EXIT_RC(NETWORK_RECONNECTED); 00099 } 00100 } 00101 00102 pClient->clientData.currentReconnectWaitInterval *= 2; 00103 00104 if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { 00105 FUNC_EXIT_RC(NETWORK_RECONNECT_TIMED_OUT_ERROR); 00106 } 00107 countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); 00108 FUNC_EXIT_RC(rc); 00109 } 00110 00111 static IoT_Error_t _aws_iot_mqtt_keep_alive(AWS_IoT_Client *pClient) { 00112 IoT_Error_t rc = IOT_SUCCESS; 00113 TimerAWS timer; 00114 size_t serialized_len; 00115 00116 FUNC_ENTRY; 00117 00118 if(NULL == pClient) { 00119 FUNC_EXIT_RC(NULL_VALUE_ERROR); 00120 } 00121 00122 if(0 == pClient->clientData.keepAliveInterval) { 00123 FUNC_EXIT_RC(IOT_SUCCESS); 00124 } 00125 00126 if(!has_timer_expired(&pClient->pingTimer)) { 00127 FUNC_EXIT_RC(IOT_SUCCESS); 00128 } 00129 00130 if(pClient->clientStatus.isPingOutstanding) { 00131 rc = _aws_iot_mqtt_handle_disconnect(pClient); 00132 FUNC_EXIT_RC(rc); 00133 } 00134 00135 /* there is no ping outstanding - send one */ 00136 init_timer(&timer); 00137 00138 countdown_ms(&timer, pClient->clientData.commandTimeoutMs); 00139 serialized_len = 0; 00140 rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 00141 PINGREQ, &serialized_len); 00142 if(IOT_SUCCESS != rc) { 00143 FUNC_EXIT_RC(rc); 00144 } 00145 00146 /* send the ping packet */ 00147 rc = aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); 00148 if(IOT_SUCCESS != rc) { 00149 //If sending a PING fails we can no longer determine if we are connected. In this case we decide we are disconnected and begin reconnection attempts 00150 rc = _aws_iot_mqtt_handle_disconnect(pClient); 00151 FUNC_EXIT_RC(rc); 00152 } 00153 00154 pClient->clientStatus.isPingOutstanding = true; 00155 /* start a timer to wait for PINGRESP from server */ 00156 countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); 00157 00158 FUNC_EXIT_RC(IOT_SUCCESS); 00159 } 00160 00161 /** 00162 * @brief Yield to the MQTT client 00163 * 00164 * Called to yield the current thread to the underlying MQTT client. This time is used by 00165 * the MQTT client to manage PING requests to monitor the health of the TCP connection as 00166 * well as periodically check the socket receive buffer for subscribe messages. Yield() 00167 * must be called at a rate faster than the keepalive interval. It must also be called 00168 * at a rate faster than the incoming message rate as this is the only way the client receives 00169 * processing time to manage incoming messages. 00170 * This is the internal function which is called by the yield API to perform the operation. 00171 * Not meant to be called directly as it doesn't do validations or client state changes 00172 * 00173 * @param pClient Reference to the IoT Client 00174 * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. 00175 * 00176 * @return An IoT Error Type defining successful/failed client processing. 00177 * If this call results in an error it is likely the MQTT connection has dropped. 00178 * iot_is_mqtt_connected can be called to confirm. 00179 */ 00180 static IoT_Error_t _aws_iot_mqtt_internal_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { 00181 IoT_Error_t yieldRc = IOT_SUCCESS; 00182 00183 uint8_t packet_type; 00184 ClientState clientState; 00185 TimerAWS timer; 00186 init_timer(&timer); 00187 countdown_ms(&timer, timeout_ms); 00188 00189 FUNC_ENTRY; 00190 00191 // evaluate timeout at the end of the loop to make sure the actual yield runs at least once 00192 do { 00193 clientState = aws_iot_mqtt_get_client_state(pClient); 00194 if(CLIENT_STATE_PENDING_RECONNECT == clientState) { 00195 if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { 00196 yieldRc = NETWORK_RECONNECT_TIMED_OUT_ERROR; 00197 break; 00198 } 00199 yieldRc = _aws_iot_mqtt_handle_reconnect(pClient); 00200 /* Network reconnect attempted, check if yield timer expired before 00201 * doing anything else */ 00202 continue; 00203 } 00204 00205 yieldRc = aws_iot_mqtt_internal_cycle_read(pClient, &timer, &packet_type); 00206 if(IOT_SUCCESS == yieldRc) { 00207 yieldRc = _aws_iot_mqtt_keep_alive(pClient); 00208 } else { 00209 // SSL read and write errors are terminal, connection must be closed and retried 00210 if(NETWORK_SSL_READ_ERROR == yieldRc || NETWORK_SSL_READ_TIMEOUT_ERROR == yieldRc 00211 || NETWORK_SSL_WRITE_ERROR == yieldRc || NETWORK_SSL_WRITE_TIMEOUT_ERROR == yieldRc) { 00212 yieldRc = _aws_iot_mqtt_handle_disconnect(pClient); 00213 } 00214 } 00215 00216 if(NETWORK_DISCONNECTED_ERROR == yieldRc) { 00217 pClient->clientData.counterNetworkDisconnected++; 00218 if(1 == pClient->clientStatus.isAutoReconnectEnabled) { 00219 yieldRc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, 00220 CLIENT_STATE_PENDING_RECONNECT); 00221 if(IOT_SUCCESS != yieldRc) { 00222 FUNC_EXIT_RC(yieldRc); 00223 } 00224 00225 pClient->clientData.currentReconnectWaitInterval = AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL; 00226 countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); 00227 /* Depending on timer values, it is possible that yield timer has expired 00228 * Set to rc to attempting reconnect to inform client that autoreconnect 00229 * attempt has started */ 00230 yieldRc = NETWORK_ATTEMPTING_RECONNECT; 00231 } else { 00232 break; 00233 } 00234 } else if(IOT_SUCCESS != yieldRc) { 00235 break; 00236 } 00237 } while(!has_timer_expired(&timer)); 00238 00239 FUNC_EXIT_RC(yieldRc); 00240 } 00241 00242 /** 00243 * @brief Yield to the MQTT client 00244 * 00245 * Called to yield the current thread to the underlying MQTT client. This time is used by 00246 * the MQTT client to manage PING requests to monitor the health of the TCP connection as 00247 * well as periodically check the socket receive buffer for subscribe messages. Yield() 00248 * must be called at a rate faster than the keepalive interval. It must also be called 00249 * at a rate faster than the incoming message rate as this is the only way the client receives 00250 * processing time to manage incoming messages. 00251 * This is the outer function which does the validations and calls the internal yield above 00252 * to perform the actual operation. It is also responsible for client state changes 00253 * 00254 * @param pClient Reference to the IoT Client 00255 * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. 00256 * 00257 * @return An IoT Error Type defining successful/failed client processing. 00258 * If this call results in an error it is likely the MQTT connection has dropped. 00259 * iot_is_mqtt_connected can be called to confirm. 00260 */ 00261 IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { 00262 IoT_Error_t rc, yieldRc; 00263 ClientState clientState; 00264 00265 if(NULL == pClient || 0 == timeout_ms) { 00266 FUNC_EXIT_RC(NULL_VALUE_ERROR); 00267 } 00268 00269 clientState = aws_iot_mqtt_get_client_state(pClient); 00270 /* Check if network was manually disconnected */ 00271 if(CLIENT_STATE_DISCONNECTED_MANUALLY == clientState) { 00272 FUNC_EXIT_RC(NETWORK_MANUALLY_DISCONNECTED); 00273 } 00274 00275 /* If we are in the pending reconnect state, skip other checks. 00276 * Pending reconnect state is only set when auto-reconnect is enabled */ 00277 if(CLIENT_STATE_PENDING_RECONNECT != clientState) { 00278 /* Check if network is disconnected and auto-reconnect is not enabled */ 00279 if(!aws_iot_mqtt_is_client_connected(pClient)) { 00280 FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); 00281 } 00282 00283 /* Check if client is idle, if not another operation is in progress and we should return */ 00284 if(CLIENT_STATE_CONNECTED_IDLE != clientState) { 00285 FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); 00286 } 00287 00288 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, 00289 CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); 00290 if(IOT_SUCCESS != rc) { 00291 FUNC_EXIT_RC(rc); 00292 } 00293 } 00294 00295 yieldRc = _aws_iot_mqtt_internal_yield(pClient, timeout_ms); 00296 00297 if(NETWORK_DISCONNECTED_ERROR != yieldRc && NETWORK_ATTEMPTING_RECONNECT != yieldRc) { 00298 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS, 00299 CLIENT_STATE_CONNECTED_IDLE); 00300 if(IOT_SUCCESS == yieldRc && IOT_SUCCESS != rc) { 00301 yieldRc = rc; 00302 } 00303 } 00304 00305 FUNC_EXIT_RC(yieldRc); 00306 } 00307 00308 #ifdef __cplusplus 00309 } 00310 #endif 00311
Generated on Tue Jul 12 2022 11:16:37 by
