Zephyr Network Connectivity API

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地址的添加

#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的数据

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收到数据后就会调用回调函数,将数据送入回调函数。

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();
	}
}

发送 链接到标题


//将接收的数据处理后组合成要发送的数据 然后由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;
}

关闭连接 链接到标题

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

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