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.
aws_iot_mqtt_client_yield.c
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 //printf("JMF: called aws_iot_mqtt_handle_disconnect\n"); 00058 rc = aws_iot_mqtt_disconnect(pClient); 00059 if(rc != AWS_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 //printf("JMF: %s:%d\n",__FILE__,__LINE__); 00071 FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); 00072 } 00073 00074 00075 static IoT_Error_t _aws_iot_mqtt_handle_reconnect(AWS_IoT_Client *pClient) { 00076 IoT_Error_t rc; 00077 00078 FUNC_ENTRY; 00079 00080 if(!has_timer_expired(&(pClient->reconnectDelayTimer))) { 00081 /* awsTimer has not expired. Not time to attempt reconnect yet. 00082 * Return attempting reconnect */ 00083 FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); 00084 } 00085 00086 rc = NETWORK_PHYSICAL_LAYER_DISCONNECTED; 00087 if(NULL != pClient->networkStack.isConnected) { 00088 rc = pClient->networkStack.isConnected(&(pClient->networkStack)); 00089 } 00090 00091 if(NETWORK_PHYSICAL_LAYER_CONNECTED == rc) { 00092 rc = aws_iot_mqtt_attempt_reconnect(pClient); 00093 if(NETWORK_RECONNECTED == rc) { 00094 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, 00095 CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); 00096 if(AWS_SUCCESS != rc) { 00097 FUNC_EXIT_RC(rc); 00098 } 00099 FUNC_EXIT_RC(NETWORK_RECONNECTED); 00100 } 00101 } 00102 00103 pClient->clientData.currentReconnectWaitInterval *= 2; 00104 00105 if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { 00106 FUNC_EXIT_RC(NETWORK_RECONNECT_TIMED_OUT_ERROR); 00107 } 00108 countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); 00109 FUNC_EXIT_RC(rc); 00110 } 00111 00112 static IoT_Error_t _aws_iot_mqtt_keep_alive(AWS_IoT_Client *pClient) { 00113 IoT_Error_t rc = AWS_SUCCESS; 00114 awsTimer timer; 00115 size_t serialized_len; 00116 00117 FUNC_ENTRY; 00118 00119 if(NULL == pClient) { 00120 FUNC_EXIT_RC(NULL_VALUE_ERROR); 00121 } 00122 00123 if(0 == pClient->clientData.keepAliveInterval) { 00124 FUNC_EXIT_RC(AWS_SUCCESS); 00125 } 00126 00127 if(!has_timer_expired(&pClient->pingTimer)) { 00128 FUNC_EXIT_RC(AWS_SUCCESS); 00129 } 00130 00131 if(pClient->clientStatus.isPingOutstanding) { 00132 //printf("JMF1\n"); 00133 rc = _aws_iot_mqtt_handle_disconnect(pClient); 00134 FUNC_EXIT_RC(rc); 00135 } 00136 00137 /* there is no ping outstanding - send one */ 00138 init_timer(&timer); 00139 00140 countdown_ms(&timer, pClient->clientData.commandTimeoutMs); 00141 serialized_len = 0; 00142 rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 00143 PINGREQ, &serialized_len); 00144 if(AWS_SUCCESS != rc) { 00145 FUNC_EXIT_RC(rc); 00146 } 00147 00148 /* send the ping packet */ 00149 rc = aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); 00150 if(AWS_SUCCESS != rc) { 00151 //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 00152 //printf("JMF2\n"); 00153 rc = _aws_iot_mqtt_handle_disconnect(pClient); 00154 FUNC_EXIT_RC(rc); 00155 } 00156 00157 pClient->clientStatus.isPingOutstanding = true; 00158 /* start a timer to wait for PINGRESP from server */ 00159 countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); 00160 00161 FUNC_EXIT_RC(AWS_SUCCESS); 00162 } 00163 00164 /** 00165 * @brief Yield to the MQTT client 00166 * 00167 * Called to yield the current thread to the underlying MQTT client. This time is used by 00168 * the MQTT client to manage PING requests to monitor the health of the TCP connection as 00169 * well as periodically check the socket receive buffer for subscribe messages. Yield() 00170 * must be called at a rate faster than the keepalive interval. It must also be called 00171 * at a rate faster than the incoming message rate as this is the only way the client receives 00172 * processing time to manage incoming messages. 00173 * This is the internal function which is called by the yield API to perform the operation. 00174 * Not meant to be called directly as it doesn't do validations or client state changes 00175 * 00176 * @param pClient Reference to the IoT Client 00177 * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. 00178 * 00179 * @return An IoT Error Type defining successful/failed client processing. 00180 * If this call results in an error it is likely the MQTT connection has dropped. 00181 * iot_is_mqtt_connected can be called to confirm. 00182 */ 00183 static IoT_Error_t _aws_iot_mqtt_internal_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { 00184 IoT_Error_t yieldRc = AWS_SUCCESS; 00185 00186 uint8_t packet_type; 00187 ClientState clientState; 00188 awsTimer timer; 00189 init_timer(&timer); 00190 countdown_ms(&timer, timeout_ms); 00191 //printf("JMF: called internal_yeld\n"); 00192 FUNC_ENTRY; 00193 00194 // evaluate timeout at the end of the loop to make sure the actual yield runs at least once 00195 do { 00196 clientState = aws_iot_mqtt_get_client_state(pClient); 00197 if(CLIENT_STATE_PENDING_RECONNECT == clientState) { 00198 //printf("JMF: pending_reconnect\n"); 00199 if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { 00200 yieldRc = NETWORK_RECONNECT_TIMED_OUT_ERROR; 00201 break; 00202 } 00203 //printf("JMF: do reconnect\n"); 00204 yieldRc = _aws_iot_mqtt_handle_reconnect(pClient); 00205 /* Network reconnect attempted, check if yield timer expired before 00206 * doing anything else */ 00207 continue; 00208 } 00209 00210 //printf("JMF: do internal_cycle_read \n"); 00211 yieldRc = aws_iot_mqtt_internal_cycle_read(pClient, &timer, &packet_type); 00212 if(AWS_SUCCESS == yieldRc) { 00213 yieldRc = _aws_iot_mqtt_keep_alive(pClient); 00214 } else { 00215 // SSL read and write errors are terminal, connection must be closed and retried 00216 if(NETWORK_SSL_READ_ERROR == yieldRc || NETWORK_SSL_READ_TIMEOUT_ERROR == yieldRc 00217 || NETWORK_SSL_WRITE_ERROR == yieldRc || NETWORK_SSL_WRITE_TIMEOUT_ERROR == yieldRc) { 00218 //printf("JMF3 %d\n",yieldRc); 00219 yieldRc = _aws_iot_mqtt_handle_disconnect(pClient); 00220 } 00221 } 00222 00223 //printf("JMF: keepalive said: %d \n", yieldRc); 00224 if(NETWORK_DISCONNECTED_ERROR == yieldRc) { 00225 //printf("JMF: was a disconnect erro \n"); 00226 pClient->clientData.counterNetworkDisconnected++; 00227 if(1 == pClient->clientStatus.isAutoReconnectEnabled) { 00228 yieldRc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, 00229 CLIENT_STATE_PENDING_RECONNECT); 00230 if(AWS_SUCCESS != yieldRc) { 00231 FUNC_EXIT_RC(yieldRc); 00232 } 00233 00234 pClient->clientData.currentReconnectWaitInterval = AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL; 00235 countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); 00236 /* Depending on timer values, it is possible that yield timer has expired 00237 * Set to rc to attempting reconnect to inform client that autoreconnect 00238 * attempt has started */ 00239 yieldRc = NETWORK_ATTEMPTING_RECONNECT; 00240 } else { 00241 break; 00242 } 00243 } else if(AWS_SUCCESS != yieldRc) { 00244 break; 00245 } 00246 } while(!has_timer_expired(&timer)); 00247 //printf("JMF: exit internal_yield with %d\n",yieldRc); 00248 00249 FUNC_EXIT_RC(yieldRc); 00250 } 00251 00252 /** 00253 * @brief Yield to the MQTT client 00254 * 00255 * Called to yield the current thread to the underlying MQTT client. This time is used by 00256 * the MQTT client to manage PING requests to monitor the health of the TCP connection as 00257 * well as periodically check the socket receive buffer for subscribe messages. Yield() 00258 * must be called at a rate faster than the keepalive interval. It must also be called 00259 * at a rate faster than the incoming message rate as this is the only way the client receives 00260 * processing time to manage incoming messages. 00261 * This is the outer function which does the validations and calls the internal yield above 00262 * to perform the actual operation. It is also responsible for client state changes 00263 * 00264 * @param pClient Reference to the IoT Client 00265 * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. 00266 * 00267 * @return An IoT Error Type defining successful/failed client processing. 00268 * If this call results in an error it is likely the MQTT connection has dropped. 00269 * iot_is_mqtt_connected can be called to confirm. 00270 */ 00271 IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { 00272 IoT_Error_t rc, yieldRc; 00273 ClientState clientState; 00274 00275 if(NULL == pClient || 0 == timeout_ms) { 00276 FUNC_EXIT_RC(NULL_VALUE_ERROR); 00277 } 00278 00279 clientState = aws_iot_mqtt_get_client_state(pClient); 00280 /* Check if network was manually disconnected */ 00281 if(CLIENT_STATE_DISCONNECTED_MANUALLY == clientState) { 00282 FUNC_EXIT_RC(NETWORK_MANUALLY_DISCONNECTED); 00283 } 00284 00285 /* If we are in the pending reconnect state, skip other checks. 00286 * Pending reconnect state is only set when auto-reconnect is enabled */ 00287 if(CLIENT_STATE_PENDING_RECONNECT != clientState) { 00288 /* Check if network is disconnected and auto-reconnect is not enabled */ 00289 if(!aws_iot_mqtt_is_client_connected(pClient)) { 00290 //printf("JMF: %s:%d\n",__FILE__,__LINE__); 00291 FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); 00292 } 00293 00294 /* Check if client is idle, if not another operation is in progress and we should return */ 00295 if(CLIENT_STATE_CONNECTED_IDLE != clientState) { 00296 FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); 00297 } 00298 00299 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, 00300 CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); 00301 if(AWS_SUCCESS != rc) { 00302 FUNC_EXIT_RC(rc); 00303 } 00304 } 00305 00306 yieldRc = _aws_iot_mqtt_internal_yield(pClient, timeout_ms); 00307 00308 if(NETWORK_DISCONNECTED_ERROR != yieldRc && NETWORK_ATTEMPTING_RECONNECT != yieldRc) { 00309 rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS, 00310 CLIENT_STATE_CONNECTED_IDLE); 00311 if(AWS_SUCCESS == yieldRc && AWS_SUCCESS != rc) { 00312 yieldRc = rc; 00313 } 00314 } 00315 00316 FUNC_EXIT_RC(yieldRc); 00317 } 00318 00319 #ifdef __cplusplus 00320 } 00321 #endif 00322
Generated on Tue Jul 12 2022 19:02:38 by
1.7.2