本文通过实现一个简单的http get演示在Zephyr下应用网络.并附带说明如何手动配置DNS server.
早期的Zephyr应用可以通过net_context API使用网络,随着Zephyr对socket支持的不断完善现在zephyr已经推荐完全使用socket进行网络应用开发而不再使用net_context. 本文通过说明如何使用socket实现一个简单的http get从openweathermap获取指定城市的天气.
配置
启用的配置项及说明如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 启用网络
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了.
代码
代码可以分为下面几部分
- 域名解析:
getaddrinfo
- 创建连接:
socket/connect
- 请求数据:
send
- 接收响应:
recv
- 关闭连接:
close
代码和说明如下: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//请求天气的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格式就是天气数据1
2
3
4
5
6
7
8
9
10
11
12
13HTTP/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方法如下:1
2
3
4
5
6
7
8
9//创建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网络使用的一个基本过程,其它的网络应用和配置都可以以此为基础展开。