【平头哥Sipeed LicheeRV 86 Panel测评】十三、利用TCP封装HTTP包请求天气信息

sonicfirr   2022-4-19 20:16 楼主

Linux还真是逐步熟悉中,现在才了解到Linux即没有原生的GUI,也没有应用层协议栈,所以要实现HTTP应用,必须利用TCP然后自己封装HTTP数据包。本篇即记录封装HTTP数据包,到心知天气请求天气信息的案例实现过程。

1、心知天气API说明

心知天气应该是当下国内使用很普遍的一个天气数据站点。相关注册和使用过程,这里就不再啰嗦了,不清楚的朋友可以自己到官网上查看(https://www.seniverse.com/)。

本例仅测试实时天气数据获取,天气相关数据只有“状态(晴朗之类)”和“气温”,请求接口地址如下:

https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c

 

image-20220419201446-1.png 13-1 实时天气请求参数说明

 

可以看到请求地址给的是域名,TCP连接需要直接给IP地址,所以用ping来获取其IP为“116.62.81.138”,端口自然是80

 

image-20220419201446-2.png  13-2 获取心知天气IP(第二个有效)

 

得到IP地址后,先不着急编程,通过网络助手实验一把,具体过程是:选择TCP Client,连接对方IP和端口(116.62.81.138:80),然后将请求地址前加上方法字串“GET”,结尾还要有两个回车换行“\r\n\r\n”。初次测试时,忘记了回车换行符没有成功,加上后就好了。

 

image-20220419201446-3.png

image-20220419201446-4.png 13-3 封装请求包并测试(第一次忘记回车符)

 

封装好的数据包是:GET https://api.thinkpage.cn/v3/weather/now.json?key=yourkey&location=tianjin&language=en&unit=c\r\n\r\n”。

2JSON分析

请求到的数据是JSON格式,贴到Json.cnhttps://www.json.cn/)的在线工具里,可以更清晰的看到其结构。

 

image-20220419201446-5.png 13-4 响应JSON包结构

 

可以看到请求实时数据(now.json),得到一个JSON对象,包含一个“results”引导的JSON数组,且数组只有一个元素,元素中又包含“location”、“now”和“last_update”三个JSON对象,内部还有键值对。

既然是开发Linux APIC程序,当然利用cJSON库来帮助进行数据解析了。本人使用的库是从网上搜到的一个百度网盘分享。

链接:https://pan.baidu.com/s/1DQynsdlNyIvsVXmf4W5b8Q

提取码:ww4z

3、请求天气案例

具体思路就是建立TCP Client连接心知天气的Server,然后发送请求包,得到响应包,解析并打印出结果,案例比较简单做成单次的——开启即运行到底,代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "cJSON.h"

#define SERVER_IP      "116.62.81.138"
#define SERVER_PORT    80
#define NOW            "now.json"
#define DAILY          "daily.json"
#define API_KEY        "yourapikey"
#define CITY           "tianjin"
#define REQ_PACK       "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&location=%s&language=en&unit=c\r\n\r\n"
#define N              1024
#define errlog(errmsg) do{ perror(errmsg);\
	                       printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\
	                       return -1;\
	                   } while(0)

//struct for weather data
typedef struct {
	char id[16];
	char name[32];
	char country[16];
	char path[64];
	char timezone[32];
	char tz_offset[16];
	char text[16];
	char code[4];
	char temp[8];
	char last_update[32];
} weather_t;

//parse function & print weather_t data function
void aita_ParseJsonNow(char *json, weather_t *w);
void aita_PrintWeather(weather_t *w);

int main(int argc, const char *argv[]) {
	int sockfd;
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);
	char sendbuf[N] = "";
	char recvbuf[N] = "";
	weather_t weather = {0};
//create socket
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		errlog("socket error");
	}
//connect to server of seniverse.com
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(SERVER_IP);
	serveraddr.sin_port = htons(SERVER_PORT);
	if((connect(sockfd, (struct sockaddr*)&serveraddr, addrlen)) < 0) {
		errlog("connect error");
	}
//build & send request package
	sprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY);
	if(send(sockfd, sendbuf, N, 0) < 0) {
		errlog("send error");
	}
//waiting server response
	if(recv(sockfd, recvbuf, N, 0) < 0) {
		errlog("recv error");
	}
	printf("recv: %s\n", recvbuf);
//parse & print data
	aita_ParseJsonNow(recvbuf, &weather);
	aita_PrintWeather(&weather);
	close(sockfd);
	return 0;
}

void aita_ParseJsonNow(char *msg, weather_t *w) {
	cJSON *json, *ja, *jo, *josub, *item;
	json = cJSON_Parse(msg); //parse string to cJSON type
	if(json == NULL) {
		printf("json type cast error: %s", cJSON_GetErrorPtr());
		return;
	} else {
		printf("parse now pack\n");
		if((ja=cJSON_GetObjectItem(json, "results")) != NULL) { //get results array
			if((jo=cJSON_GetArrayItem(ja, 0)) != NULL) {        //get array[0](the only item)
				//get location object
				if((josub=cJSON_GetObjectItem(jo, "location")) != NULL) {
					if((item=cJSON_GetObjectItem(josub, "id")) != NULL) {
						memcpy(w->id, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "name")) != NULL) {
						memcpy(w->name, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "country")) != NULL) {
						memcpy(w->country, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "path")) != NULL) {
						memcpy(w->path, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "timezone")) != NULL) {
						memcpy(w->timezone, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "timezone_offset")) != NULL) {
						memcpy(w->tz_offset, item->valuestring, strlen(item->valuestring));
					}
				}
				//get now object
				if((josub=cJSON_GetObjectItem(jo, "now")) != NULL) {
					if((item=cJSON_GetObjectItem(josub, "text")) != NULL) {
						memcpy(w->text, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "code")) != NULL) {
						memcpy(w->code, item->valuestring, strlen(item->valuestring));
					}
					if((item=cJSON_GetObjectItem(josub, "temperature")) != NULL) {
						memcpy(w->temp, item->valuestring, strlen(item->valuestring));
					}
				}
				//get last_update object
				if((josub=cJSON_GetObjectItem(jo, "last_update")) != NULL) {
					memcpy(w->last_update, josub->valuestring, strlen(josub->valuestring));					
				}
			}
		}
	}
	//delete original json pack free memory
	cJSON_Delete(json);
	return;
}

void aita_PrintWeather(weather_t *w) {
	printf("id: %s\n", w->id);
	printf("name: %s\n", w->name);
	printf("country: %s\n", w->country);
	printf("path: %s\n", w->path);
	printf("timezone: %s\n", w->timezone);
	printf("timezone_offset: %s\n", w->tz_offset);
	printf("text: %s\n", w->text);
	printf("code: %s\n", w->code);
	printf("temperature: %s\n", w->temp);
	printf("last_update: %s\n", w->last_update);
}

 

项目路径中建立了源文件main.c,编写上述代码,并导入cJSON.ccJSON.h,编译命令为:“riscv64-unknown-linux-gnu-gcc main.c cJSON.c -o weather -lm”。因为cJSON会用到math库,而它需要“-lm”来动态链接。

 

image-20220419201446-6.png 13-5 编译项目

 

image-20220419201446-7.png 13-6 执行相关效果(控制台输出)

 

 

 

 

 

本帖最后由 sonicfirr 于 2022-4-19 20:21 编辑

回复评论 (2)

建立TCP Client连接心知天气的Server,然后发送请求包,得到响应包,很好

点赞  2022-4-21 07:31

cJson用起来还是挺麻烦的,可是C语言能做这样已经很不错了。json还是java,javascript等带虚拟机的语言好用。

默认摸鱼,再摸鱼。2022、9、28
点赞  2022-4-21 22:40
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复