'\" t .\" Title: coap_observe .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 11/09/2019 .\" Manual: libcoap Manual .\" Source: coap_observe 4.2.1 .\" Language: English .\" .TH "COAP_OBSERVE" "3" "11/09/2019" "coap_observe 4\&.2\&.1" "libcoap Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" coap_observe, coap_resource_set_get_observable, coap_resource_notify_observers, coap_resource_get_uri_path, coap_find_observer, coap_delete_observer, coap_delete_observers \- work with CoAP observe .SH "SYNOPSIS" .sp \fB#include \fR .sp \fBvoid coap_resource_set_get_observable(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, int \fR\fB\fImode\fR\fR\fB);\fR .sp \fBint coap_resource_notify_observers(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, const const_string_t *\fR\fB\fIquery\fR\fR\fB);\fR .sp \fBconst coap_string_t *coap_resource_get_uri_path(coap_resource_t *\fR\fB\fIresource\fR\fR\fB);\fR .sp \fBcoap_subscription_t *coap_find_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const const_string_t *\fR\fB\fItoken\fR\fR\fB);\fR .sp \fBint coap_delete_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const coap_binary_t *\fR\fB\fItoken\fR\fR\fB);\fR .sp \fBvoid coap_delete_observers(coap_context_t *\fR\fB\fIcontext\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB);\fR .sp Link with \fB\-lcoap\-2\fR, \fB\-lcoap\-2\-gnutls\fR, \fB\-lcoap\-2\-openssl\fR or \fB\-lcoap\-2\-tinydtls\fR depending on your (D)TLS library type\&. .SH "DESCRIPTION" .sp RFC 7641 extends the CoAP protocol to be able to monitor the state of a resource over time\&. .sp This enables clients to "observe" resources with a defined query, i\&.e\&., to retrieve a representation of a resource and keep this representation updated by the server over a period of time\&. .sp The server has to flag a resource as "observable", and then the client has to request in a GET request that it wants to observe this resource by the use of the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_ESTABLISH\&. Optionally, the client can specify query options for the resource\&. .sp To remove the "observe" subscription, the client has to issue a GET request with the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_CANCEL\&. Alternatively, the server can remove a subscription by calling \fBcoap_delete_observer\fR() or \fBcoap_delete_observers\fR(), but this does not notify the client that the subscription has been removed\&. .sp The underlying library adds in and removes "subscribers" to the resource as appropriate in the server side logic\&. .sp Within the server application, it needs to determine that there is a change of state of the resource under observation, and then cause the CoAP library layer to initiate a "fake GET request" so that an observe GET response gets sent back to all the clients that are observing the resource\&. The appropriate GET handler within the server application is called to fill in the response packet with the appropriate information\&. This "fake GET request" is triggered by a call to \fBcoap_resource_notify_observers\fR()\&. .sp The call to \fBcoap_run_once\fR() in the main server application i/o loop will do all the necessary processing of sending any outstanding "fake GET requests"\&. .sp Whenever the server sends a copy of the state of the "observed" resource to the client, it will use the same token used by the client when the client requested the "observe"\&. The client will receive this observe response in the handler defined by \fBcoap_register_response_handler\fR(3)\&. It is the responsibility of the client application to match the supplied token and update the appropriate internal information\&. .sp The \fBcoap_resource_set_get_observable\fR() function enables or disables the observable status of the \fIresource\fR by the setting of \fImode\fR\&. If \fImode\fR is 1, then the \fIresource\fR is observable\&. If \fImode\fR is 0, then the \fIresource\fR is no longer observable\&. .sp \fBNOTE:\fR It is not possible for the Unknown Resource, created by \fBcoap_resource_unknown_init\fR(3), to be observable as the Uri\-Path is not known when libcoap creates a "fake GET request"\&. The Unknown Resource PUT handler must create a new resource and mark the resource as "observable" if a specific resource needs to be observable\&. The application must then manage the deleteion of the resource at the appropriate time\&. .sp \fBNOTE:\fR The type (confirmable or non\-confirmable) of the triggered observe GET response is determined not by the initial GET request, but independently by the server as per RFC 7641 3\&.5\&. Transmission\&. This is controlled by the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON or COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource using \fBcoap_resource_init\fR(3)\&. Furthermore, the server must send at least one "observe" response as confirmable, when generally sending non\-confirmable, every 24 hours\&. libcoap handles this by sending every fifth (COAP_OBS_MAX_NON) response as a confirmable response for detection that the client is still responding\&. .sp The \fBcoap_resource_notify_observers\fR() function needs to be called whenever the server application determines that there has been a change to the state of \fIresource\fR, possibly only matching a specific \fIquery\fR if \fIquery\fR is not NULL\&. .sp The \fBcoap_resource_get_uri_path\fR() function is used to obtain the UriPath of the \fIresource\fR definion\&. .sp The \fBcoap_find_observer\fR() function is used to check whether the current GET request as determined from \fIresource\fR, \fIsession\fR and \fItoken\fR is currently being observed or not\&. .sp The \fBcoap_delete_observer\fR() function deletes the specific observer associated with \fIresource\fR, \fIsession\fR and has \fItoken\fR\&. .sp The \fBcoap_delete_observers\fR() function is used to delete all observers associated with the \fIsession\fR that is a part of the \fIcontext\fR\&. .SH "RETURN VALUES" .sp The \fBcoap_resource_get_uri_path\fR() function returns the uri_path or NULL if there was a failure\&. .sp The \fBcoap_find_observer\fR() function returns the subscription or NULL if there was a failure\&. .sp The \fBcoap_resource_set_get_observable\fR() function return 0 on failure, 1 on success\&. .sp The \fBcoap_delete_observer\fR() function return 0 on failure, 1 on success\&. .SH "EXAMPLES" .sp \fBSimple Time Server\fR .sp .if n \{\ .RS 4 .\} .nf #include coap_resource_t *time_resource = NULL; static int check_if_time_resource_has_changed(coap_resource_t *resource) { return 1; } /* specific GET "time" handler, called from hnd_get_generic() */ static void hnd_get_time(coap_context_t *context, coap_resource_t *resource, coap_session_t *session, coap_pdu_t *request, coap_string_t *token, coap_string_t *query, coap_pdu_t *response) { unsigned char buf[40]; size_t len; time_t now; /* \&.\&.\&. Additional analysis code for resource, request pdu etc\&. \&.\&.\&. */ /* After analysis, generate a suitable response */ /* Note that token, if set, is already in the response pdu */ now = time(NULL); if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) { /* Output secs since Jan 1 1970 */ len = snprintf((char *)buf, sizeof(buf), "%lu", now); } else { /* Output human\-readable time */ struct tm *tmp; tmp = gmtime(&now); if (!tmp) { /* If \*(Aqnow\*(Aq is not valid */ response\->code = COAP_RESPONSE_CODE(404); return; } len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp); } /* * Invoke coap_add_data_blocked_response() to do all the hard work\&. * * Define the format \- COAP_MEDIATYPE_TEXT_PLAIN \- to add in * Define how long this response is valid for (secs) \- 1 \- to add in\&. * * OBSERVE Option added internally if needed within the function * BLOCK2 Option added internally if output too large * ETAG Option added internally */ coap_add_data_blocked_response(resource, session, request, response, token, COAP_MEDIATYPE_TEXT_PLAIN, 1, len, buf); /* * As resource\->code has been updated in coap_add_data_blocked_response(), * the response pdu will be transmitted by the underlying library\&. */ } /* Generic GET handler */ static void hnd_get_generic(coap_context_t *ctx, coap_resource_t *resource, coap_session_t *session, coap_pdu_t *request, coap_string_t *token, coap_string_t *query, coap_pdu_t *response) { coap_str_const_t *uri_path = coap_resource_get_uri_path(resource); if (!uri_path) { /* Unexpected Failure */ response\->code = COAP_RESPONSE_CODE(400); return; } /* Is this the "time" resource" ? */ if (coap_string_equal(uri_path, coap_make_str_const("time"))) { hnd_get_time(ctx, resource, session, request, token, query, response); return; } /* Other resources code */ /* Failure response */ response\->code = COAP_RESPONSE_CODE(400); } /* Initialize generic GET handler */ static void init_resources(coap_context_t *ctx) { coap_resource_t *r; /* Create a resource to return return or update time */ r = coap_resource_init(coap_make_str_const("time"), COAP_RESOURCE_FLAGS_NOTIFY_CON); /* We are using a generic GET handler here */ coap_register_handler(r, COAP_REQUEST_GET, hnd_get_generic); coap_resource_set_get_observable(r, 1); coap_add_resource(ctx, r); time_resource = r; } int main(int argc, char *argv[]){ coap_context_t *ctx = NULL; coap_endpoint_t *ep = NULL; coap_address_t addr; unsigned wait_ms; /* Create the libcoap context */ ctx = coap_new_context(NULL); if (!ctx) { exit(1); } coap_address_init(&addr); addr\&.addr\&.sa\&.sa_family = AF_INET; addr\&.addr\&.sin\&.sin_port = ntohs(COAP_DEFAULT_PORT); ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP); /* Other Set up Code */ init_resources(ctx); wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; while (1) { int result = coap_run_once( ctx, wait_ms ); if ( result < 0 ) { break; } else if ( result && (unsigned)result < wait_ms ) { /* decrement if there is a result wait time returned */ wait_ms \-= result; } else { /* * result == 0, or result >= wait_ms * (wait_ms could have decremented to a small value, below * the granularity of the timer in coap_run_once() and hence * result == 0) */ time_t t_now = time(NULL); if (t_last != t_now) { /* Happens once per second */ int i; t_last = t_now; if (time_resource) { coap_resource_notify_observers(time_resource, NULL); } } if (result) { /* result must have been >= wait_ms, so reset wait_ms */ wait_ms = COAP_RESOURCE_CHECK_TIME * 1000; } } } exit(0); } .fi .if n \{\ .RE .\} .sp \fBClient Observe Request Setup\fR .sp .if n \{\ .RS 4 .\} .nf #include /* Usually, requests are sent confirmable */ static unsigned char msgtype = COAP_MESSAGE_CON; static unsigned int token = 0; static coap_pdu_t * coap_new_request(coap_context_t *context, coap_session_t *session, char request_code, coap_optlist_t **options, unsigned char *data, size_t length, int observe) { coap_pdu_t *pdu; coap_optlist_t *opt; (void)context; /* Create the pdu with the appropriate options */ pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session), coap_session_max_pdu_size(session)); if (!pdu) return NULL; /* * Create uniqueness token for this request for handling unsolicited / * delayed responses */ token++; if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) { coap_log(LOG_DEBUG, "cannot add token to request\en"); goto error; } if (request_code == COAP_REQUEST_GET && observe) { /* Indicate that we want to observe this resource */ if (!coap_insert_optlist(options, coap_new_optlist(COAP_OPTION_OBSERVE, COAP_OBSERVE_ESTABLISH, NULL))) goto error; } /* \&.\&.\&. Other code / options etc\&. \&.\&.\&. */ /* Add in all the options (after internal sorting) to the pdu */ if (!coap_add_optlist_pdu(pdu, options)) goto error; if (data && length) { /* Add in the specified data */ if (!coap_add_data(pdu, length, data)) goto error; } return pdu; error: coap_delete_pdu(pdu); return NULL; } .fi .if n \{\ .RE .\} .SH "SEE ALSO" .sp \fBcoap_attribute\fR(3), \fBcoap_context\fR(3), \fBcoap_handler\fR(3), \fBcoap_pdu_setup\fR(3) and \fBcoap_resource\fR(3) .SH "FURTHER INFORMATION" .sp "RFC7252: The Constrained Application Protocol (CoAP)" .sp "RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)" .SH "BUGS" .sp Please report bugs on the mailing list for libcoap: libcoap\-developers@lists\&.sourceforge\&.net .SH "AUTHORS" .sp The libcoap project