// -*- mode:doc; -*-
// vim: set syntax=asciidoc,tw=0:
coap_io(3)
==========
:doctype: manpage
:man source: coap_io
:man version: @PACKAGE_VERSION@
:man manual: libcoap Manual
NAME
----
coap_io,
coap_io_process,
coap_io_process_with_fds,
coap_context_get_coap_fd,
coap_io_prepare_io,
coap_io_do_io,
coap_io_prepare_epoll,
coap_io_do_epoll,
coap_io_can_exit
- Work with CoAP I/O to do the packet send and receives
SYNOPSIS
--------
*#include <coap@LIBCOAP_API_VERSION@/coap.h>*
*int coap_io_process(coap_context_t *_context_, uint32_t _timeout_ms_)*;
*int coap_io_process_with_fds(coap_context_t *_context_,
uint32_t _timeout_ms_, int _nfds_, fd_set *_readfds_, fd_set *_writefds_,
fd_set *_exceptfds_)*;
*int coap_context_get_coap_fd(const coap_context_t *_context_)*;
*unsigned int coap_io_prepare_io(coap_context_t *_context_,
coap_socket_t *_sockets_[], unsigned int _max_sockets_,
unsigned int *_num_sockets_, coap_tick_t _now_)*;
*void coap_io_do_io(coap_context_t *_context_, coap_tick_t _now_)*;
*unsigned int coap_io_prepare_epoll(coap_context_t *_context_,
coap_tick_t _now_)*;
*void coap_io_do_epoll(coap_context_t *_context_, struct epoll_event *_events_,
size_t _nevents_)*;
*int coap_can_exit(coap_context_t *_context_)*;
For specific (D)TLS library support, link with
*-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*,
*-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*
or *-lcoap-@LIBCOAP_API_VERSION@-tinydtls*. Otherwise, link with
*-lcoap-@LIBCOAP_API_VERSION@* to get the default (D)TLS library support.
DESCRIPTION
-----------
After setting up all the contexts, resources, endpoints sessions etc., the
underlying CoAP and (D)TLS need to send (and possible re-send) created packets
as well as receive packets for processing.
The *coap_io_process*() function will process any outstanding packets to send
for the specified _context_, process any available input packets and then wait
for processing any new input packets, or for when to re-transmit a packet, for
up to _timeout_ms_ milli-seconds before returning. There are 2 special case
_timeout_ms_ values.
[source, c]
----
#define COAP_IO_WAIT 0
#define COAP_IO_NO_WAIT ((uint32_t)-1)
----
If _timeout_ms_ is set to COAP_IO_WAIT, then *coap_io_process*() will block
until the next internal action (e.g. packet retransmit) if any, or block until
the next packet is received whichever is the sooner and do the necessary
processing. If _timeout_ms_ is set to COAP_IO_NO_WAIT, then *coap_io_process*()
will return immediately after processing without waiting for any new input
packets to arrive.
*NOTE:* *coap_io_process*() should not be called from within a callback
handler as defined using the coap_register_*_handler() as *coap_io_process*()
will likely recursively call the same handler.
There are two methods of how to call *coap_io_process*().
1. Have *coap_io_process*() called from within a while() loop. Under idle
conditions (no input traffic) *coap_io_process*() will then get called every
_timeout_ms_, but more frequently if there is input / retransmission traffic.
2. Wait on the file descriptor returned by *coap_context_get_coap_fd*()
using *select*(), *poll*() or an event returned by epoll_wait(). If 'read' is
available on the CoAP file descriptor, call *coap_io_process*() with
_timeout_ms_ set to COAP_IO_NO_WAIT. +
*NOTE*: This second method is only available for environments that support epoll
(mostly Linux) with libcoap compiled to use *epoll* (the default) as libcoap
will then be using *epoll* internally to process all the file descriptors of
the different sessions.
See EXAMPLES below.
The *coap_io_process*() function is the primary function applications should
use. There are internal functions that *coap_io_process*() calls which are
available to use if absolutely necessary. These internal functions and how to
use them is different depending on whether libcoap has been compiled to use
*epoll* (Linux systems only) or not.
For *epoll* libcoap, *coap_io_process*() in simple terms calls
*coap_io_prepare_epoll*(), does an *epoll_wait*() and then calls
*coap_io_do_epoll*() if needed to make sure that all event based i/o has been
completed.
For *non-epoll* libcoap, *coap_io_process*() in simple terms calls
*coap_io_prepare_io*() to set up sockets[], sets up all of the *select*()
parameters based on the COAP_SOCKET_WANT* values in the sockets[], does a
*select*(), updates the sockets[] with COAP_SOCKET_CAN_* as appropriate and
then calls *coap_io_do_io*() to make sure that all current i/o has been
completed.
The *coap_io_prepare_epoll*() function for the specified _context_ will
iterate through the endpoints and sessions to transmit any triggered observer
responses as well as handling any timed out packet re-transmissions. Returned,
based on _now_, is the number of milli-secs needed to delay until the next
time that *coap_io_prepare_epoll*() needs to get called. After this call an
*epoll_wait*() should done.
The *coap_io_do_epoll*() function for the specified _context_ will
iterate through the _nevents_ of _events_ returned by *epoll_wait*() and
execute the appropriate low level i/o function to send / receive / process the
packets. Where appropriate, structure information (endpoints, sessions etc.)
is updated with the value of _now_ in the lower level functions.
The *coap_io_prepare_io*() function for the specified _context_ will iterate
through the endpoints and sessions to add all of sockets waiting for network
traffic (COAP_SOCKET_WANT_* is set) found to _sockets_ (limited by
_max_sockets_) and updates _num_sockets_ with the number of sockets found.
Furthermore, any triggered observer responses are transmitted
as well as handling any timed out packet re-transmissions. Returned, based on
_now_, is the number of milli-secs needed to delay until the next time that
*coap_io_prepare_io*() needs to get called. After this call a *select*() should
done on all the file descriptors (COAP_WANT_READ for readfds etc.), and any
that are returned active should set the appropriate COAP_SOCKET_CAN_* in the
_sockets_.
The *coap_io_do_io*() function for the specified _context_ will
iterate through the endpoints and sessions to find all of sockets that have
COAP_SOCKET_CAN_* set and then execute the appropriate low level i/o function
to send / receive / process the packets. Where appropriate, structure
information (endpoints, sessions etc.) is updated with the value of _now_ in
the lower level functions.
The *coap_io_process_with_fds*() function is the same as *coap_process_io*()
but supports additional select() style parameters _nfds_, _readfds_,
_writefds_ and _exceptfds_. This provides the ability to add in additional
non libcoap FDs to test for in the internal select() call which can then
tested after the return from coap_io_process_with_fds(). _readfds_,
_writefds_ and _exceptfds_ can either point to a defined and pre-filled fd_set
structure or NULL if not required. _nfds_ needs to be set to the maximum FD to
test for in _readfds_, _writefds_ or _exceptfds_ if any of them are set plus 1.
If none of them are set, then _nfds_ should be set to 0.
NOTE: The additional parameters for *coap_io_process_with_fds*() are only used
if there is no epoll support in libcoap. If there is epoll support, then
*coap_context_get_coap_fd*() should be used and this returned FD along with
other non libcoap FDs can separately be monitored using method 2 above.
The *coap_context_get_coap_fd*() function obtains from the specified
_context_ a single file descriptor that can be monitored by a *select*() or
as an event returned from a *epoll_wait*() call. This file descriptor will get
updated with information (read, write etc. available) whenever any of the
internal to libcoap file descriptors (sockets) change state.
The *coap_can_exit*() function checks to see if there are any outstanding
PDUs to transmit associated with _context_ and returns 1 if there is nothing
outstanding else 0. This function does not check that all requests transmitted
have been responded to.
RETURN VALUES
-------------
*coap_io_process*() and *coap_io_process_with_fds*() returns the time, in
milli-seconds, that was spent in the function. If -1 is returned, there was
an unexpected error.
*coap_context_get_coap_fd*() returns a non-negative number as the file
descriptor to monitor, or -1 if epoll is not configured in libcoap.
*coap_io_prepare_io*() and *coap_io_prepare_epoll*() returns the number of
milli-seconds that need to be waited before the function should next be called.
*coap_can_exit*() returns 1 if there is nothing outstanding to transmit else
returns 0.
EXAMPLES
--------
*Method One - use coap_io_process()*
[source, c]
----
#include <coap@LIBCOAP_API_VERSION@/coap.h>
int main(int argc, char *argv[]){
coap_context_t *ctx = NULL;
unsigned wait_ms;
/* Remove (void) definition if variable is used */
(void)argc;
(void)argv;
/* Create the libcoap context */
ctx = coap_new_context(NULL);
if (!ctx) {
exit(1);
}
/* See coap_block(3) */
coap_context_set_block_mode(ctx,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
/* Other Set up Code */
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
while (1) {
int result = coap_io_process(ctx, wait_ms);
if (result < 0) {
/* There is an internal issue */
break;
}
/* Do any other housekeeping */
}
coap_free_context(ctx);
/* Do any other cleanup */
exit(0);
}
----
*Method One - coap_io_process_with_fds*
[source, c]
----
#include <coap@LIBCOAP_API_VERSION@/coap.h>
int main(int argc, char *argv[]){
coap_context_t *ctx = NULL;
unsigned wait_ms;
fd_set readfds;
int nfds = 0;
/* Remove (void) definition if variable is used */
(void)argc;
(void)argv;
/* Create the libcoap context */
ctx = coap_new_context(NULL);
if (!ctx) {
exit(1);
}
/* See coap_block(3) */
coap_context_set_block_mode(ctx,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
FD_ZERO(&readfds);
/* Set up readfds and nfds to handle other non libcoap FDs */
/* Other Set up Code */
wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
while (1) {
int result = coap_io_process_with_fds(ctx, wait_ms, nfds, &readfds, NULL, NULL);
if (result < 0) {
/* There is an internal issue */
break;
}
/* Check if set non libcoap FDs and process accordingly */
/* Do any other housekeeping */
}
coap_free_context(ctx);
/* Do any other cleanup */
exit(0);
}
----
*Method Two - select() based on monitorable file descriptor*
[source, c]
----
#include <coap@LIBCOAP_API_VERSION@/coap.h>
#include <errno.h>
int main(int argc, char *argv[]){
coap_context_t *ctx = NULL;
int coap_fd;
fd_set m_readfds;
int nfds;
/* Remove (void) definition if variable is used */
(void)argc;
(void)argv;
/* Create the libcoap context */
ctx = coap_new_context(NULL);
if (!ctx) {
exit(1);
}
/* See coap_block(3) */
coap_context_set_block_mode(ctx,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
coap_fd = coap_context_get_coap_fd(ctx);
if (coap_fd == -1) {
/* epoll is not supported */
exit(1);
}
FD_ZERO(&m_readfds);
FD_SET(coap_fd, &m_readfds);
nfds = coap_fd + 1;
/* Other Set up Code */
while (1) {
fd_set readfds = m_readfds;
int result;
/* Wait until any i/o takes place */
result = select (nfds, &readfds, NULL, NULL, NULL);
if (result == -1) {
if (errno != EAGAIN) {
coap_log(LOG_DEBUG, "select: %s (%d)\n", coap_socket_strerror(), errno);
break;
}
}
if (result > 0) {
if (FD_ISSET(coap_fd, &readfds)) {
result = coap_io_process(ctx, COAP_IO_NO_WAIT);
if (result < 0) {
/* There is an internal issue */
break;
}
}
}
/* Do any other housekeeping */
}
coap_free_context(ctx);
/* Do any other cleanup */
exit(0);
}
----
*Method Two - epoll_wait() based on monitorable file descriptor*
[source, c]
----
#include <coap@LIBCOAP_API_VERSION@/coap.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAX_EVENTS 10
int main(int argc, char *argv[]){
coap_context_t *ctx = NULL;
int coap_fd;
int epoll_fd;
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];
int nevents;
int i;
/* Remove (void) definition if variable is used */
(void)argc;
(void)argv;
/* Create the libcoap context */
ctx = coap_new_context(NULL);
if (!ctx) {
exit(1);
}
/* See coap_block(3) */
coap_context_set_block_mode(ctx,
COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY);
coap_fd = coap_context_get_coap_fd(ctx);
if (coap_fd == -1) {
exit(1);
}
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
exit(2);
}
ev.events = EPOLLIN;
ev.data.fd = coap_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, coap_fd, &ev) == -1) {
exit(3);
}
/* Other Set up Code */
while (1) {
int result;
/* Wait until any i/o takes place */
nevents = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nevents == -1) {
if (errno != EAGAIN) {
coap_log(LOG_DEBUG, "epoll_wait: %s (%d)\n", coap_socket_strerror(), errno);
break;
}
}
for (i = 0; i < nevents; i++) {
if (events[i].data.fd == coap_fd) {
result = coap_io_process(ctx, COAP_IO_NO_WAIT);
if (result < 0) {
/* There is an internal issue */
break;
}
}
else {
/* Process other events */
}
}
/* Do any other housekeeping */
}
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, coap_fd, &ev) == -1) {
coap_log(LOG_DEBUG, "epoll_ctl: %s (%d)\n", coap_socket_strerror(), errno);
}
coap_free_context(ctx);
/* Do any other cleanup */
exit(0);
}
----
SEE ALSO
--------
*coap_block*(3) and *coap_context*(3)
FURTHER INFORMATION
-------------------
See "RFC7252: The Constrained Application Protocol (CoAP)" for further
information.
BUGS
----
Please report bugs on the mailing list for libcoap:
libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at
https://github.com/obgm/libcoap/issues
AUTHORS
-------
The libcoap project <libcoap-developers@lists.sourceforge.net>