Zephyr Network Connectivity API

Creative Commons
本作品采用知识共享署名

Zephyr提供一组connectivity API访问网络,和Socket类似可以通过Connectivity API可以create & close连接,接受和发送数据(包括TCP和UDP)。不一样的是Connectivity API使用的是 Fragment buffer,而Socket使用的是Linear buffer。

Connectivity API定义在include/net/net_context.h
Fragment buffer定义在include/net/buf.h

Connectivity API

只列了示例中api,更多参见头文件net_pkt.h,net_core.h,net_context.h或者文档
net_ipaddr_copy: ip地址复制
net_if_get_default:获取默认的iface,网卡控制接口
net_if_ipv4_addr_add:配置iface IPV4地址

net_context_get:获取tcp/ip context,作为网络控制句柄
net_context_bind: 绑定context通讯地址和端口
net_context_recv: 注册一个回调函数用于接收context上的网络封包
net_context_sendto:向指定的context发送网络封包
net_context_put:关闭一个context

net_pkt_get_tx: 获取一个pkt用于填装要发送的网络封包
net_pkt_appdatalen:获取指定pkt的封包应用层数据的长度
net_pkt_appdata:获取指定pkt的封包应用层数据指针
net_pkt_get_data:获取指定pkg的封包net_buf
net_pkt_family:获取pkt的family(ipv6 or v4)
net_pkt_unref: 释放一个pkg

net_buf_frags_len: 获取net_buf的data buf长度
net_buf_pull:从net_buf的最开始移除指定长度的数据
net_buf_add:向net_buf添加指定长度的数据(只移动指针,并返回需要添加数据的头指针)
net_buf_frag_add:将指定的net_buf加入到net_pkt中
net_buf_frag_del:从net_pkt中删除指定的net_buf

Sample

void main(void)
{
NET_INFO(“Run sample application”);

init_app();

create_context();

bind_address();

receive_data();

k_sem_take(&waiter, K_FOREVER);

close_context();

NET_INFO("Stopping sample application");

}
使用connectivity API进行网络通行,可以分为以下几个步骤

  1. 初始化
  2. 建立连接
  3. 接收&发送数据
  4. 关闭连接

初始化

初始化主要是完成ip地址的添加

1
2
3
4
5
6
7
8
9
10
11
#define MY_IPADDR {{ { 192, 0, 2, 1 } } }
static struct in_addr my_addr = MY_IPADDR;

static inline void init_app(void)
{
k_sem_init(&waiter, 0, 1);

/* Add our address to the network interface */
net_if_ipv4_addr_add(net_if_get_default(), &my_addr,
NET_ADDR_MANUAL, 0);
}

建立连接

使用net_context_get获取ip stack的context,再将要通信的ip地址和端口绑定上去,这里要接受所有ip地址的数据因此用INADDR_ANY,只接受端口5683的数据

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
static int create_context(void)
{
ret = net_context_get(AF_INET4, SOCK_DGRAM, IPPROTO_UDP, &context); //获取UDP的context
if (!ret) {
NET_ERR("Cannot get context (%d)", ret);
return ret;
}

return 0;
}

#define INADDR_ANY 0
#define INADDR_ANY_INIT { { { INADDR_ANY } } }

static int bind_address(void)
{
static struct sockaddr_in any_addr = {
.sin_family = AF_INET,
.sin_addr = INADDR_ANY_INIT,
.sin_port = htons(5683) };

ret = net_context_bind(context, (struct sockaddr *) &any_addr, sizeof(any_addr));
if (ret < 0) {
NET_ERR("Could not bind the context\n");
return ret;
}

return 0;
}

数据接收和发送

接收

数据接收是将接收回调函数注册到network stack,当network stack收到数据后就会调用回调函数,将数据送入回调函数。

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
static int receive_data(void)
{
ret = net_context_recv(context, udp_received, 0, NULL); //这里注册回调函数,用于接受udp数据(context是前面获取的udp context)
if (ret < 0) {
NET_ERR("Cannot receive IPv4 UDP packets");

quit();

return ret;
}

return 0;
}

//network stack收到udp后放到net_buf中调用udp_received处理
static void udp_received(struct net_context *context,
struct net_buf *buf,
int status,
void *user_data)
{
struct net_pkt *reply_pkt;
struct sockaddr dst_addr;
sa_family_t family = net_pkt_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);

//处理接收到的buf包,并组合成要发送的数据reply_pkt
reply_pkt = udp_recv(dbg, context, buf);

//当数据使用完后需要用net_pkt_unref将stack的数据包释放掉
net_pkt_unref(buf);

//将reply_pkt发送出去
ret = net_context_sendto(reply_pkt, &dst_addr, udp_sent, 0,
UINT_TO_POINTER(net_buf_frags_len(reply_pkt->frags)),
user_data);
if (ret < 0) {
NET_ERR("Cannot send data to peer (%d)", ret);

net_pkt_unref(reply_pkt);

quit();
}
}

发送

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

//将接收的数据处理后组合成要发送的数据 然后由net_context_sendto发送出去
static struct net_pkt *udp_recv(const char *name,
struct net_context *context,
struct net_buf *buf)
{
struct net_buf *frag, *tmp;
struct net_pkt *reply_pkt;
int header_len, recv_len, reply_len;

NET_INFO("%s received %u bytes", name,
net_pkt_appdatalen(buf));

//获取一个tx pkt
reply_pkt = net_pkt_get_tx(context, K_FOREVER);

NET_ASSERT(reply_pkt);

recv_len = net_buf_frags_len(buf->frags);

//将接收buf的数据放到tmp中
tmp = buf->frags;

/* First fragment will contain IP header so move the data
* down in order to get rid of it.
*/
header_len = net_pkt_appdata(buf) - tmp->data;

NET_ASSERT(header_len < CONFIG_NET_BUF_DATA_SIZE);

net_buf_pull(tmp, header_len);

while (tmp) {
//获取一个net_buf包frag
frag = net_pkt_get_data(context, K_FOREVER);

//将要回应的数据从tmp copy到frag中
memcpy(net_buf_add(frag, tmp->len), tmp->data, tmp->len);

将frag关联到send pkt中
net_buf_frag_add(reply_pkt, frag);

net_buf_frag_del(buf, tmp);

tmp = buf->frags;
}

reply_len = net_buf_frags_len(reply_pkt->frags);

NET_ASSERT_INFO(recv_len != reply_len,
"Received %d bytes, sending %d bytes",
recv_len, reply_len);

return reply_pkt;
}

关闭连接

数据收发完毕后要将连接关闭

1
2
3
4
5
6
7
8
9
10
static int close_context(void)
{
ret = net_context_put(context);
if (ret < 0) {
NET_ERR("Cannot close IPv6 UDP context");
return ret;
}

return 0;
}

参考

http://docs.zephyrproject.org/subsystems/networking/networking-api-usage.html
http://docs.zephyrproject.org/api/networking.html