Zephyr网络使用-socket使用

本文通过实现一个简单的http get演示在Zephyr下应用网络.并附带说明如何手动配置DNS server.

早期的Zephyr应用可以通过net_context API使用网络,随着Zephyr对socket支持的不断完善现在zephyr已经推荐完全使用socket进行网络应用开发而不再使用net_context. 本文通过说明如何使用socket实现一个简单的http get从openweathermap获取指定城市的天气.

配置 链接到标题

启用的配置项及说明如下

# 启用网络
CONFIG_NETWORKING=y

# http要走tcp,因此启用ipv4和tcp
CONFIG_NET_IPV4=y
CONFIG_NET_TCP=y

# 要通过dhcp拿ip地址
CONFIG_NET_DHCPV4=y

# http get要通过域名去拿数据,socket只接收ip地址,因此要开启DNS
CONFIG_DNS_RESOLVER=y

# 开启socket
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y

zephyr内原生的socket API是以zsock_为前缀开头,例如connect就是zsock_connect,为了增加应用的可移植性,zephyr对其进行了封装,当开启CONFIG_NET_SOCKETS_POSIX_NAMES=y后zephyr就支持标准的socket API了.

代码 链接到标题

代码可以分为下面几部分

  1. 域名解析: getaddrinfo
  2. 创建连接: socket/connect
  3. 请求数据: send
  4. 接收响应: recv
  5. 关闭连接: close 代码和说明如下:
//请求天气的API域名
#define HTTP_HOST       "api.openweathermap.org"
//请求天气的动作,下面的[app_id], 需要替换为你自己再openweathermap的app id
#define HTTP_PATH       "/data/2.5/weather?q=Chengdu,CN&units=metric&APPID=[app_id]"
//http使用80口
#define HTTP_PORT "80"

// 组合成http get的字符串
#define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n"

#define SSTRLEN(s) (sizeof(s) - 1)
#define CHECK(r) { if (r == -1) { printk("Error: " #r "\n"); return 0; } }

static int http_get(void)
{
	static struct addrinfo hints;
	struct addrinfo *res;
	int st, sock;

    //对api.openweathermap.org:80进行域名解析,解析的结构放到res中
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
    st = getaddrinfo(HTTP_HOST, HTTP_PORT, &hints, &res);
    printk("st = %d\n", st);
	if (st != 0) {
		return 0;
	}

    //创建socket
	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	CHECK(sock);
	printk("sock = %d\n", sock);

    //连接到http服务器
	CHECK(connect(sock, res->ai_addr, res->ai_addrlen));

    printk("Request:\n\n");
    //发送http get的请求
	CHECK(send(sock, REQUEST, SSTRLEN(REQUEST), 0));


	printk("Response:\n\n");

	while (1) {
        //接收响应数据
		int len = recv(sock, response, sizeof(response) - 1, 0);

		if (len < 0) {
			printk("Error reading response\n");
			return 0;
		}

		if (len == 0) {
			break;
		}

        //将数据打印出来
		response[len] = 0;
		printk("%s", response);
	}

	printk("\n");
    //关闭连接
	(void)close(sock);

	return 0;
}

上面代码执行后就可以在串口看到拿到带有http header的数据, 后面跟的json格式就是天气数据

HTTP/1.1 200 OK
Server: openresty
Date: Thu, 27 Oct 2022 06:23:34 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 468
Connection: close
X-Cache-Key: /data/2.5/weather?APPID=xxxxxxxxxxxxxxxxxxxx&q=chengdu,cn&units=metric
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST

{"coord":{"lon":104.0667,"lat":30.6667},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":16.94,"feels_like":16.99,"temp_min":16.94,"temp_max":16.94,"pressure":1021,"humidity":88},"visibility":10000,"wind":{"speed":3,"deg":10},"clouds":{"all":75},"dt":1666851763,"sys":{"type":1,"id":9674,"country":"CN","sunrise":1666826049,"sunse
t":1666866053},"timezone":28800,"id":1815286,"name":"Chengdu","cod":200}

从上面的代码可以看出完全使用的标准的posix socket API没有zephyr的API,这意味着使用posix socket API编写的网络应用可以很轻松的移植到zephyr上。

关于手动DNS 链接到标题

当Zephyr使用DHCP拿到ip地址后会自动去配置DNS server,如果是手动设置ip地址,就需要通过代码手动去配置DNS,否则getaddrinfo将返回-3无法进行域名解析. 手动配置DNS server方法如下:

//创建dns server list, 注意,一定要以NULL作为list的结尾
char *dns1 = "192.168.137.1";
char *dns2 = "8.8.8.8";
char *server_list[] = {dns1, dns2, NULL};

//配置DNS server
int status = dns_resolve_reconfigure(dns_resolve_get_default(),
							server_list,
							NULL);

小节 链接到标题

Zephyr网络使用-Wifi控制Zephyr网络使用-IP配置两篇文章加上本文,演示了从物理连接,协议栈配置启用,网络应用三个层次的配置和使用,涵盖了zephyr网络使用的一个基本过程,其它的网络应用和配置都可以以此为基础展开。