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_pending, coap_io_get_fds, coap_can_exit, coap_socket_get_fd, coap_socket_get_flags, coap_socket_set_flags - Work with CoAP I/O to do the packet send and receives
SYNOPSIS
#include <coap3/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_io_pending(coap_context_t *context);
unsigned int coap_io_get_fds(coap_context_t *context, coap_fd_t read_fds[],
unsigned int *have_read_fds, unsigned int max_read_fds, coap_fd_t write_fds[],
unsigned int *have_write_fds, unsigned int max_write_dfs,
unsigned int *rem_timeout_ms);
int coap_can_exit(coap_context_t *context);
coap_fd_t coap_socket_get_fd(coap_socket_t *socket);
coap_socket_flags_t coap_socket_get_flags(coap_socket_t *socket);
void coap_socket_set_flags(coap_socket_t *socket, coap_socket_flags_t flags);
For specific (D)TLS library support, link with
-lcoap-3-notls, -lcoap-3-gnutls,
-lcoap-3-openssl, -lcoap-3-mbedtls,
-lcoap-3-wolfssl
or -lcoap-3-tinydtls. Otherwise, link with
-lcoap-3 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 possibly re-send) created packets
as well as receive packets for processing.
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.
FUNCTIONS
Function: coap_io_process()
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.
#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().
-
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.
-
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.
:NOTE:* With multi-threading protection enabled, it is possible to
call coap_io_process() from multiple threads to do some load balancing.
See EXAMPLES below.
Function: coap_io_prepare_epoll()
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.
Function: coap_io_do_epoll()
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.
Function: coap_io_prepare_io()
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.
Function: coap_io_do_io()
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.
Function: coap_io_process_with_fds()
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.
Function: coap_context_get_coap_fd()
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.
Function: coap_io_pending()
The coap_io_pending() function checks to see if there are any outstanding
i/o requests / responses associated with context as well as if Observe has
been set up (client only) and large transfers are in process.
Function: coap_io_get_fds()
The coap_io_get_fds() function is used to get all of the libcoap internally
used file descriptors associated with context in a read or write pending state.
read_fds[] is a defined array to hold all the file descriptors that are in a
read pending state with a size of max_read_fds. have_read_fds is returned
with the number of file descriptors in read_fds[].
write_fds[] is a defined array to hold all the file descriptors that are in a
write pending state with a size of max_write_fds. have_write_fds is returned
with the number of file descriptors in write_fds[].
rem_timeout_ms is updated with the remaining milli-seconds before coap_io_process()
needs to be called again to handle any internal timeouts. If rem_timeout_ms is 0,
then there is no timeout and the next event will be a change in state of one of
the file descriptors.
Function: coap_can_exit()
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.
Function: coap_socket_get_fd()
The coap_socket_get_fd() function obtains the file descriptor from the given
socket. The file descriptor can be used to integrate libcoap in an external
event loop instead of using one of its builtin event loops.
Function: coap_socket_get_flags()
The coap_socket_get_flags() function obtains the event flags from the
given socket.
Function: coap_socket_set_flags()
The coap_socket_set_flags() function sets the event flags for the
given socket.
EXAMPLES
Method One - use coap_io_process()
#include <coap3/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;
/* Initialize libcoap library */
coap_startup();
/* 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);
coap_cleanup();
/* Do any other cleanup */
exit(0);
}
Method One - coap_io_process_with_fds
#include <coap3/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;
/* Initialize libcoap library */
coap_startup();
/* 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);
coap_cleanup();
/* Do any other cleanup */
exit(0);
}
Method Two - select() based on monitorable file descriptor
#include <coap3/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;
/* Initialize libcoap library */
coap_startup();
/* 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_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);
coap_cleanup();
/* Do any other cleanup */
exit(0);
}
Method Two - epoll_wait() based on monitorable file descriptor
#include <coap3/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;
/* Initialize libcoap library */
coap_startup();
/* 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_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_debug("epoll_ctl: %s (%d)\n", coap_socket_strerror(), errno);
}
coap_free_context(ctx);
coap_cleanup();
/* Do any other cleanup */
exit(0);
}