Network Connectivity API¶
Applications can use the connectivity API defined in net_context.h
to create a connection, send or receive data, and close a connection.
The same API can be used when working with UDP or TCP data.
The net_context API is similar to the BSD socket API and mapping between these
two is possible. The main difference between net_context API and BSD socket
API is that the net_context API uses the fragmented network buffers (net_buf)
defined in net/buf.h
and BSD socket API uses linear memory buffers.
This example creates a simple server that listens to incoming UDP connections and sends the received data back. You can download the example application source file here connectivity-example-app.c
This example application begins with some initialization. (Use this as an example; you may need to do things differently in your own application.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #define SYS_LOG_DOMAIN "example-app"
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define NET_DEBUG 1
#include <zephyr.h>
#include <net/nbuf.h>
#include <net/net_core.h>
#include <net/net_context.h>
#define MY_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0x1 } } }
#define MY_PORT 4242
struct in6_addr in6addr_my = MY_IP6ADDR;
struct sockaddr_in6 my_addr6 = { 0 };
struct net_context *context;
int ret;
struct nano_sem quit_lock;
static inline void quit(void)
{
nano_sem_give(&quit_lock);
}
static inline void init_app(void)
{
nano_sem_init(&quit_lock);
/* Add our address to the network interface */
net_if_ipv6_addr_add(net_if_get_default(), &in6addr_my,
NET_ADDR_MANUAL, 0);
}
void main(void)
{
NET_INFO("Run sample application");
init_app();
create_context();
bind_address();
receive_data();
nano_sem_take(&quit_lock, TICKS_UNLIMITED);
close_context();
NET_INFO("Stopping sample application");
}
|
After initialization, first thing application needs to create a context. Context is similar to a socket.
1 2 3 4 5 6 7 8 9 10 | static int create_context(void)
{
ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &context);
if (!ret) {
NET_ERR("Cannot get context (%d)", ret);
return ret;
}
return 0;
}
|
Then you need to define the local end point for a connection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static int bind_address(void)
{
net_ipaddr_copy(&my_addr6.sin6_addr, &in6addr_my);
my_addr6.sin6_family = AF_INET6;
my_addr6.sin6_port = htons(MY_PORT);
ret = net_context_bind(context, (struct sockaddr *)&my_addr6);
if (ret < 0) {
NET_ERR("Cannot bind IPv6 UDP port %d (%d)",
ntohs(my_addr6.sin6_port), ret);
return ret;
}
return 0;
}
|
Wait until the connection data is received.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #define MAX_DBG_PRINT 64
static struct net_buf *udp_recv(const char *name,
struct net_context *context,
struct net_buf *buf)
{
struct net_buf *reply_buf, *frag, *tmp;
int header_len, recv_len, reply_len;
NET_INFO("%s received %u bytes", name,
net_nbuf_appdatalen(buf));
reply_buf = net_nbuf_get_tx(context, K_FOREVER);
NET_ASSERT(reply_buf);
recv_len = net_buf_frags_len(buf->frags);
tmp = buf->frags;
/* First fragment will contain IP header so move the data
* down in order to get rid of it.
*/
header_len = net_nbuf_appdata(buf) - tmp->data;
NET_ASSERT(header_len < CONFIG_NET_NBUF_DATA_SIZE);
net_buf_pull(tmp, header_len);
while (tmp) {
frag = net_nbuf_get_data(context, K_FOREVER);
memcpy(net_buf_add(frag, tmp->len), tmp->data, tmp->len);
net_buf_frag_add(reply_buf, frag);
net_buf_frag_del(buf, tmp);
tmp = buf->frags;
}
reply_len = net_buf_frags_len(reply_buf->frags);
NET_ASSERT_INFO(recv_len != reply_len,
"Received %d bytes, sending %d bytes",
recv_len, reply_len);
return reply_buf;
}
static inline void udp_sent(struct net_context *context,
int status,
void *token,
void *user_data)
{
if (!status) {
NET_INFO("Sent %d bytes", POINTER_TO_UINT(token));
}
}
static inline void set_dst_addr(sa_family_t family,
struct net_buf *buf,
struct sockaddr *dst_addr)
{
if (family == AF_INET6) {
net_ipaddr_copy(&net_sin6(dst_addr)->sin6_addr,
&NET_IPV6_BUF(buf)->src);
net_sin6(dst_addr)->sin6_family = AF_INET6;
net_sin6(dst_addr)->sin6_port = NET_UDP_BUF(buf)->src_port;
}
}
static void udp_received(struct net_context *context,
struct net_buf *buf,
int status,
void *user_data)
{
struct net_buf *reply_buf;
struct sockaddr dst_addr;
sa_family_t family = net_nbuf_family(buf);
static char dbg[MAX_DBG_PRINT + 1];
int ret;
snprintf(dbg, MAX_DBG_PRINT, "UDP IPv%c",
family == AF_INET6 ? '6' : '4');
set_dst_addr(family, buf, &dst_addr);
reply_buf = udp_recv(dbg, context, buf);
net_nbuf_unref(buf);
ret = net_context_sendto(reply_buf, &dst_addr, udp_sent, 0,
UINT_TO_POINTER(net_buf_frags_len(reply_buf)),
user_data);
if (ret < 0) {
NET_ERR("Cannot send data to peer (%d)", ret);
net_nbuf_unref(reply_buf);
quit();
}
}
static int receive_data(void)
{
ret = net_context_recv(context, udp_received, 0, NULL);
if (ret < 0) {
NET_ERR("Cannot receive IPv6 UDP packets");
quit();
return ret;
}
return 0;
}
|
Close the context when finished.
1 2 3 4 5 6 7 8 9 10 11 | /* Context close */
static int close_context(void)
{
ret = net_context_put(context);
if (ret < 0) {
NET_ERR("Cannot close IPv6 UDP context");
return ret;
}
return 0;
}
|