Skip to content

Commit

Permalink
lwm2mclient: improve overall stability upon connectivity issues
Browse files Browse the repository at this point in the history
This patch adds a few simple mechanisms to improve management of
flaky connectivity conditions:

 - Add timeout to the socket "connect" function. Each client will
 timeout after the value passed to the "lwm2m_client_service" function
 for all clients (e.g. if 1000 ms timeout is passed to the "service"
 function with X clients, each of the X client will use 1000 ms as the
 timeout value when calling "connect")

 - Remove call to the "service" function in the "start" function. It
 makes the "start" function non-blocking, connection to th server is now
 initiated during first call of the "service" function

 - Remove dynamic allocation of handles array in the service function,
 using static allocation instead. This brings a limitation that the
 "service" function can not be called for servicing more than a limited
 number of 128 clients

 - Add detection of connection failure in new places, returning an error
 for the concerned client to the calling application

Change-Id: I0af9f60780ba6c0c59c2f08f005a1ae5b9f405b7
Signed-off-by: Gregory Lemercier <g.lemercier@samsung.com>
  • Loading branch information
Gregory Lemercier committed Dec 19, 2017
1 parent 8977467 commit cc74496
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 43 deletions.
2 changes: 1 addition & 1 deletion core/internals.h
Expand Up @@ -237,7 +237,7 @@ lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP, lwm2m_uri_t * u
coap_status_t registration_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
void registration_deregister(lwm2m_context_t * contextP, lwm2m_server_t * serverP);
void registration_freeClient(lwm2m_client_t * clientP);
uint8_t registration_start(lwm2m_context_t * contextP);
uint8_t registration_start(lwm2m_context_t * contextP, time_t * timeoutP);
void registration_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP);
lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP);

Expand Down
2 changes: 1 addition & 1 deletion core/liblwm2m.c
Expand Up @@ -415,7 +415,7 @@ int lwm2m_step(lwm2m_context_t * contextP,
break;
#endif
case STATE_REGISTER_REQUIRED:
result = registration_start(contextP);
result = registration_start(contextP, timeoutP);
if (COAP_NO_ERROR != result) return result;
contextP->state = STATE_REGISTERING;
break;
Expand Down
2 changes: 1 addition & 1 deletion core/liblwm2m.h
Expand Up @@ -120,7 +120,7 @@ void lwm2m_printf(const char * format, ...);
// Returns a session handle that MUST uniquely identify a peer.
// secObjInstID: ID of the Securty Object instance to open a connection to
// userData: parameter to lwm2m_init()
void * lwm2m_connect_server(uint16_t secObjInstID, void * userData);
void * lwm2m_connect_server(uint16_t secObjInstID, void * userData, int timeout);
// Close a session created by lwm2m_connect_server()
// sessionH: session handle identifying the peer (opaque to the core)
// userData: parameter to lwm2m_init()
Expand Down
8 changes: 4 additions & 4 deletions core/registration.c
Expand Up @@ -173,7 +173,7 @@ static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP,

// send the registration for a single server
static uint8_t prv_register(lwm2m_context_t * contextP,
lwm2m_server_t * server)
lwm2m_server_t * server, time_t timeout)
{
char query[200];
int query_length;
Expand Down Expand Up @@ -202,7 +202,7 @@ static uint8_t prv_register(lwm2m_context_t * contextP,

if (server->sessionH == NULL)
{
server->sessionH = lwm2m_connect_server(server->secObjInstID, contextP->userData);
server->sessionH = lwm2m_connect_server(server->secObjInstID, contextP->userData, (int)timeout);
}

if (NULL == server->sessionH) return COAP_503_SERVICE_UNAVAILABLE;
Expand Down Expand Up @@ -367,7 +367,7 @@ time_t lwm2m_last_registration(lwm2m_context_t * contextP,
}
}

uint8_t registration_start(lwm2m_context_t * contextP)
uint8_t registration_start(lwm2m_context_t * contextP, time_t * timeoutP)
{
lwm2m_server_t * targetP;
uint8_t result;
Expand All @@ -380,7 +380,7 @@ uint8_t registration_start(lwm2m_context_t * contextP)
if (targetP->status == STATE_DEREGISTERED
|| targetP->status == STATE_REG_FAILED)
{
result = prv_register(contextP, targetP);
result = prv_register(contextP, targetP, *timeoutP);
}
targetP = targetP->next;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/akc_client/akc_client.c
Expand Up @@ -286,7 +286,7 @@ int main(int argc, char *argv[])

while (!quit)
{
int ret = lwm2m_client_service(client, 100);
int ret = lwm2m_client_service(client, 1000);
if ((ret == LWM2M_CLIENT_QUIT) || (ret == LWM2M_CLIENT_ERROR))
break;

Expand Down
49 changes: 23 additions & 26 deletions examples/lwm2mclient/lwm2mclient.c
Expand Up @@ -86,6 +86,7 @@

#define MAX_PACKET_SIZE 1024
#define MAX_CONNECTION_RETRIES 5
#define MAX_NUMBER_CLIENTS 128

typedef struct {
coap_protocol_t proto;
Expand Down Expand Up @@ -152,7 +153,7 @@ extern uint8_t firmware_change_object(lwm2m_data_t *dataArray, lwm2m_object_t *o
extern uint8_t location_change_object(lwm2m_data_t *dataArray, lwm2m_object_t *object);
extern uint8_t connectivity_moni_change(lwm2m_data_t *dataArray, lwm2m_object_t *object);

void * lwm2m_connect_server(uint16_t secObjInstID, void * userData)
void * lwm2m_connect_server(uint16_t secObjInstID, void * userData, int timeout)
{
client_data_t * dataP = NULL;
char * uri = NULL;
Expand Down Expand Up @@ -199,15 +200,17 @@ void * lwm2m_connect_server(uint16_t secObjInstID, void * userData)
port++;

#ifdef WITH_LOGS
fprintf(stdout, "\r\nOpening connection to server at %s:%s\r\n", host, port);
fprintf(stdout, "\r\nOpening connection to server at %s:%s with timeout %d ms\r\n",
host, port, timeout);
#endif

// If secure connection, make sure we have a security object
instance = LWM2M_LIST_FIND(dataP->securityObjP->instanceList, secObjInstID);
if (instance == NULL) goto exit;

conn = connection_create(protocol, dataP->root_ca, dataP->verify_cert, dataP->use_se,
dataP->sock, host, dataP->local_port, port, dataP->addressFamily, securityObj, instance->id);
conn = connection_create(protocol, dataP->root_ca, dataP->verify_cert,
dataP->use_se, dataP->sock, host, dataP->local_port, port,
dataP->addressFamily, securityObj, instance->id, timeout);
if (!conn)
{
#ifdef WITH_LOGS
Expand Down Expand Up @@ -347,6 +350,8 @@ static void poll_lwm2m_sockets(client_data_t **clients, int number_clients, int
#ifdef WITH_LOGS
fprintf(stderr, "server has closed the connection\r\n");
#endif
close(data->sock);
data->sock = -1;
}
}

Expand Down Expand Up @@ -654,15 +659,6 @@ client_handle_t* lwm2m_client_start(object_container_t *init_val, char *root_ca,
goto error;
}

/* Service once to initialize the first steps */
if (lwm2m_client_service(handle, 0) <= LWM2M_CLIENT_OK)
{
#ifdef WITH_LOGS
printf("Failed to connect LWM2M server.\n");
#endif
goto error;
}

return handle;

error:
Expand Down Expand Up @@ -775,29 +771,21 @@ int lwm2m_client_service(client_handle_t *handle, int timeout_ms)
int lwm2m_clients_service(client_handle_t **handles, int number_handles, int timeout_ms)
{
time_t min_timeout = 60;
client_data_t **clients = NULL;
client_data_t *clients[MAX_NUMBER_CLIENTS];
int number_clients = 0;
int i = 0;

if (!handles || number_handles <= 0) {
if (!handles || number_handles <= 0 || number_handles > MAX_NUMBER_CLIENTS) {
#ifdef WITH_LOGS
fprintf(stderr, "lwm2m_clients_service: wrong parameters\r\n");
#endif
return LWM2M_CLIENT_ERROR;
}

clients = lwm2m_malloc(sizeof(client_data_t *)*number_handles);
if (!clients) {
#ifdef WITH_LOGS
fprintf(stderr, "lwm2m_clients_service: Out of memory");
#endif
return LWM2M_CLIENT_ERROR;
}

for (i = 0; i < number_handles; i++)
{
int result;
time_t timeout = 60;
time_t timeout = timeout_ms;
client_data_t *data = (client_data_t *)handles[i]->client;
result = lwm2m_step(data->lwm2mH, &timeout);
min_timeout = min_timeout > timeout ? timeout : min_timeout;
Expand Down Expand Up @@ -827,7 +815,7 @@ int lwm2m_clients_service(client_handle_t **handles, int number_handles, int tim
handles[i]->error = LWM2M_CLIENT_ERROR;
continue;
}
connection_restart(data->conn);
connection_restart(data->conn, timeout_ms);
min_timeout = 0;
data->connection_retries++;
continue;
Expand All @@ -839,7 +827,16 @@ int lwm2m_clients_service(client_handle_t **handles, int number_handles, int tim

min_timeout = min_timeout * 1000 < timeout_ms || timeout_ms == 0 ? min_timeout * 1000 : timeout_ms;
poll_lwm2m_sockets(clients, number_clients, min_timeout);
lwm2m_free(clients);

/* Check if some connections got closed, set error accordingly */
for (i = 0; i < number_handles; i++)
{
client_data_t *data = (client_data_t *)handles[i]->client;

if (data->sock <= 0)
handles[i]->error = LWM2M_CLIENT_ERROR;
}

return min_timeout;
}

Expand Down
4 changes: 2 additions & 2 deletions examples/shared/connection.h
Expand Up @@ -104,9 +104,9 @@ connection_t *connection_find(connection_t *connList, struct sockaddr_storage *a

connection_t *connection_create(coap_protocol_t protocol, char *root_ca, bool verify_cert,
bool use_se, int sock, char *host, char *local_port, char *remote_port, int addressFamily,
lwm2m_object_t * obj, int instanceId);
lwm2m_object_t * obj, int instanceId, int timeout);

int connection_restart(connection_t *conn);
int connection_restart(connection_t *conn, int timeout);

void connection_free(connection_t * connList);

Expand Down
60 changes: 53 additions & 7 deletions examples/shared/openssl_connection.c
Expand Up @@ -608,7 +608,7 @@ static char *security_get_secret_key(lwm2m_object_t * obj, int instanceId, int *
}
}

int connection_restart(connection_t *conn)
int connection_restart(connection_t *conn, int timeout)
{
int sock;
connection_t *newConn = NULL;
Expand Down Expand Up @@ -654,7 +654,8 @@ int connection_restart(connection_t *conn)
conn->remote_port,
conn->address_family,
conn->sec_obj,
conn->sec_inst);
conn->sec_inst,
timeout);

if (!newConn)
{
Expand Down Expand Up @@ -784,16 +785,20 @@ connection_t * connection_create(coap_protocol_t protocol,
char *remote_port,
int addressFamily,
lwm2m_object_t * sec_obj,
int sec_inst)
int sec_inst,
int timeout)
{
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
int s;
int s, ret;
struct sockaddr *sa;
socklen_t sl;
connection_t * connP = NULL;
long arg = 0;
int flags = 0;
fd_set rset, wset;
struct timeval ts;

memset(&hints, 0, sizeof(hints));
hints.ai_family = addressFamily;
Expand Down Expand Up @@ -827,11 +832,52 @@ connection_t * connection_create(coap_protocol_t protocol,
sa = p->ai_addr;
sl = p->ai_addrlen;

if (-1 == connect(s, p->ai_addr, p->ai_addrlen))
/* We go non-blocking mode to set timeout on connect */
flags = fcntl(s, F_GETFL, 0);
fcntl(s, F_SETFL, flags | O_NONBLOCK);

#ifdef WITH_LOGS
fprintf(stderr, "Try to connect to server with timeout %d ms\n", timeout);
#endif
ret = connect(s, p->ai_addr, p->ai_addrlen);
if (ret < 0)
{
close(s);
s = -1;
if (errno != EINPROGRESS)
{
#ifdef WITH_LOGS
fprintf(stderr, "Connect to socket failed (err=%d)\n", errno);
#endif
goto fail;
}

FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(s, &rset);
FD_SET(s, &wset);
ts.tv_sec = timeout / 1000;
ts.tv_usec = (timeout - (ts.tv_sec * 1000)) * 1000;
ret = select(s + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL);
if (ret <= 0)
{
#ifdef WITH_LOGS
fprintf(stderr, "Waiting for socket failed (err=%d)\n", ret);
#endif
goto fail;
}

if (!FD_ISSET(s, &rset) && !FD_ISSET(s, &wset))
{
#ifdef WITH_LOGS
fprintf(stderr, "No fd was set\n");
#endif
goto fail;
}
}

continue;
fail:
close(s);
s = -1;
}
}

Expand Down

0 comments on commit cc74496

Please sign in to comment.