目录
引言
在嵌入式开发或网络编程学习中,通过Socket编程实现与Web API的交互是一项基础而重要的技能。本文介绍了我开发的一个基于HTTP协议的天气预报查询系统。该系统通过以下流程获取数据:
-
建立TCP连接至远程服务器(nowapi提供的API接口)
-
发送符合规范的HTTP请求
-
接收并解析JSON格式的响应数据
-
提取并展示指定城市的实时天气及未来天气预报
下文将详细解析系统实现的技术细节,包括Socket通信机制、HTTP协议处理、JSON数据解析等核心模块的实现方案。
1.系统架构与核心功能
该系统采用模块化设计,主要包含以下功能模块:
-
网络通信模块:建立TCP连接并发送HTTP请求
-
数据解析模块:处理JSON格式的天气数据
-
数据展示模块:格式化输出天气信息
程序通过Weather结构体存储天气数据:
cpp
typedef struct {
char city[32];
char date[32];
char week[32];
char weather[32];
char max_temperature[4];
char min_temperature[4];
char wind_direction[32];
char wind_power[8];
char Humidity[8];
} Weather;
2.关键技术实现
2.1网络通信
-
使用
socket建立TCP连接 -
通过
connect连接nowapi服务器(103.205.5.206:80) -
构造符合HTTP协议的GET请求
cpp
int CreateTcpConnection(const char *pip, int port) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// ...套接字创建和连接逻辑
}
int SendHttpRequest(int sockfd, const char *purl) {
sprintf(sendbuf, "GET %s HTTP/1.1\r\n", purl);
// ...HTTP头构造逻辑
}
2.2JSON数据解析
-
使用cJSON库解析API返回的JSON数据
-
分别处理当天和未来天气数据
-
错误处理机制确保数据有效性
cpp
int Analys_today(char* json_string) {
cJSON *root = cJSON_Parse(json_string);
cJSON *success = cJSON_GetObjectItem(root, "success");
if(0 == strcmp(success->valuestring,"0")) {
printf("该地点未收到天气数据\n");
exit(0);
}
// ...数据提取逻辑
}
2.3用户交互与展示
-
终端交互式输入城市名称
-
格式化输出当天和未来天气信息
-
清晰的数据分隔线提升可读性
cpp
int ShowToday() {
printf("------------------------------------------------\n");
printf("城市 : %s\n",Today.city);
printf("日期 : %s\n",Today.date);
// ...其他天气信息输出
printf("------------------------------------------------\n");
}
3.完整工作流程
-
用户输入目标城市名称
-
程序创建TCP连接并发送天气请求:
-
当天天气:
/?app=weather.today&weaid=城市名... -
未来天气:
/?app=weather.future&weaid=城市名...
-
-
接收并解析JSON格式的天气数据
-
格式化输出天气信息:
-
当天天气详情
-
未来7天天气预报
-
4.技术亮点
-
高效的网络通信:直接使用socket进行TCP通信,避免依赖外部库
-
健壮的错误处理:检测API返回的success字段,无效数据时优雅退出
-
内存管理:使用cJSON_Delete释放解析后的JSON对象
-
数据封装:Weather结构体清晰组织天气数据
5.成果展示
5.1代码
cpp
#include "head.h"
#include "cJSON.h"
typedef struct {
char city[32];
char date[32];
char week[32];
char weather[32];
char max_temperature[4];
char min_temperature[4];
char wind_direction[32];
char wind_power[8]; //风力级数
char Humidity[8]; //湿度
}Weather;
Weather Today ;
Weather FutureDay[7] = {0};
int Num_future_days = 0;
int CreateTcpConnection(const char *pip, int port)
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in seraddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("fail to socket");
return -1;
}
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(port);
seraddr.sin_addr.s_addr = inet_addr(pip);
ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
perror("fail to connect");
return -1;
}
return sockfd;
}
int SendHttpRequest(int sockfd, const char *purl) //发送http请求
{
char sendbuf[4096] = {0};
ssize_t nret = 0;
sprintf(sendbuf, "GET %s HTTP/1.1\r\n", purl);
sprintf(sendbuf, "%sHost: api.k780.com\r\n", sendbuf);
sprintf(sendbuf, "%sUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n", sendbuf);
sprintf(sendbuf, "%sAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n", sendbuf);
sprintf(sendbuf, "%sAccept-Language: en-US,en;q=0.5\r\n", sendbuf);
sprintf(sendbuf, "%sAccept-Encoding: gzip, deflate\r\n", sendbuf);
sprintf(sendbuf, "%sConnection: keep-alive\r\n", sendbuf);
sprintf(sendbuf, "%sUpgrade-Insecure-Requests: 1\r\n", sendbuf);
sprintf(sendbuf, "%s\r\n", sendbuf);
nret = send(sockfd, sendbuf, strlen(sendbuf), 0);
if (-1 == nret)
{
perror("fail to send");
return -1;
}
return 0;
}
int RecvHttpRespone(int sockfd, char *ptmpbuff, int maxlen)
{
ssize_t nret = 0;
nret = recv(sockfd, ptmpbuff, maxlen, 0);
if (-1 == nret)
{
perror("fail to recv");
return -1;
}
//printf("%s\n\n",ptmpbuff);
return 0;
}
int ShowToday( )
{
printf("----------------------今日天气-------------------\n");
printf("------------------------------------------------\n");
printf("城市 : %s\n",Today.city);
printf("日期 : %s\n",Today.date);
printf("星期 : %s\n",Today.week);
printf("天气 : %s\n",Today.weather);
printf("最高温度 :%s℃\n",Today.max_temperature);
printf("最低温度 :%s℃\n",Today.min_temperature);
printf("湿度 : %s\n",Today.Humidity);
printf("风向 : %s\n",Today.wind_direction);
printf("风力 : %s\n",Today.wind_power);
printf("------------------------------------------------\n");
return 0;
}
int ShowFuture()
{
printf("----------------------未来天气-------------------\n");
// 遍历数组
for(int i = 1; i < Num_future_days; i++)
{
printf("------------------------------------------------\n");
printf("日期 : %s\n",FutureDay[i].date);
printf("星期 : %s\n",FutureDay[i].week);
printf("天气 : %s\n",FutureDay[i].weather);
printf("最高温度 : %s℃\n",FutureDay[i].max_temperature);
printf("最低温度 : %s℃\n",FutureDay[i].min_temperature);
printf("风向 : %s\n",FutureDay[i].wind_direction);
printf("风力 : %s\n",FutureDay[i].wind_power);
printf("------------------------------------------------\n");
}
return 0;
}
// 解析JSON字符串
int Analys_today(char* json_string)
{
cJSON *root = cJSON_Parse(json_string);
if (!root)
{
printf("解析失败\n");
return -1;
}
cJSON *success = cJSON_GetObjectItem(root, "success");
if(0 == strcmp(success -> valuestring,"0"))
{
printf("================================================\n");
printf("该地点未收到天气数据\n");
printf("================================================\n");
exit(0);//优雅退出进程
}
cJSON *result = cJSON_GetObjectItem(root, "result");
// 获取今日的结果
strcpy(Today.date , cJSON_GetObjectItem(result, "days")->valuestring);
strcpy(Today.city , cJSON_GetObjectItem(result, "citynm")->valuestring);
strcpy(Today.week , cJSON_GetObjectItem(result, "week")->valuestring);
strcpy(Today.weather , cJSON_GetObjectItem(result, "weather")->valuestring);
strcpy(Today.wind_direction , cJSON_GetObjectItem(result, "wind")->valuestring);
strcpy(Today.wind_power, cJSON_GetObjectItem(result, "winp")->valuestring);
strcpy(Today.Humidity , cJSON_GetObjectItem(result, "humidity")->valuestring);
strcpy(Today.max_temperature , cJSON_GetObjectItem(result, "temp_high")->valuestring);
strcpy(Today.min_temperature , cJSON_GetObjectItem(result, "temp_low")->valuestring);
cJSON_Delete(root);
return 0;
}
int Analys_future(char* json_string)
{
// 解析JSON字符串
char tmp[32] = {0};
cJSON *root = cJSON_Parse(json_string);
cJSON *result = cJSON_GetObjectItem(root, "result");//遍历数组
if (!root)
{
printf("解析失败\n");
return -1;
}
// 获取未来的结果
// 遍历数组
Num_future_days = cJSON_GetArraySize(result);
for (int i = 0; i < Num_future_days; i++)
{
cJSON *futureDay_tmp = cJSON_GetArrayItem(result, i);//遍历数组
//获取未来天气结果
strcpy(FutureDay[i].date , cJSON_GetObjectItem(futureDay_tmp, "days")->valuestring);
strcpy(FutureDay[i].week , cJSON_GetObjectItem(futureDay_tmp, "week")->valuestring);
strcpy(FutureDay[i].weather , cJSON_GetObjectItem(futureDay_tmp, "weather")->valuestring);
strcpy(FutureDay[i].wind_direction , cJSON_GetObjectItem(futureDay_tmp, "wind")->valuestring);
strcpy(FutureDay[i].wind_power, cJSON_GetObjectItem(futureDay_tmp, "winp")->valuestring);
strcpy(FutureDay[i].max_temperature , cJSON_GetObjectItem(futureDay_tmp, "temp_high")->valuestring);
strcpy(FutureDay[i].min_temperature , cJSON_GetObjectItem(futureDay_tmp, "temp_low")->valuestring);
}
// 释放内存
cJSON_Delete(root);
return 0;
}
int main(void)
{
int sockfd = 0;
char tmpbuff[40960] = {0};
char City[32] = {0};
char URL[256] = {0};
char* j_start = NULL;
printf("请输入您要查看的城市:\n");
scanf("%s",City);
//发送请求
sockfd = CreateTcpConnection("103.205.5.206", 80);
sprintf(URL,"/?app=weather.today&weaid=%s&appkey=78692&sign=4fc12179d774cba5500d9d7c56e92f74&format=json",City);
SendHttpRequest(sockfd, URL);
RecvHttpRespone(sockfd, tmpbuff, sizeof(tmpbuff));//接收结果
//close(sockfd);
//解析结果
j_start = strstr(tmpbuff, "\r\n\r\n");
j_start += 4; // 跳过"\r\n\r\n"
for(;*j_start != '{';j_start++);
//输出结果
Analys_today(j_start);
printf("================================================\n");
ShowToday();
//发送请求
memset(tmpbuff,0,sizeof(tmpbuff));
memset(URL,0,sizeof(URL));
sprintf(URL,"/?app=weather.future&weaid=%s&appkey=78692&sign=4fc12179d774cba5500d9d7c56e92f74&format=json",City);
SendHttpRequest(sockfd, URL);
RecvHttpRespone(sockfd, tmpbuff, sizeof(tmpbuff));//接收结果
//解析结果
j_start = strstr(tmpbuff, "\r\n\r\n");
j_start += 4; // 跳过"\r\n\r\n"
for(;*j_start != '{';j_start++);
//输出结果
Analys_future(j_start);
ShowFuture();
printf("================================================\n");
close(sockfd);
return 0;
}
5.2使用示例
cpp
//
./weather_program
请输入您要查看的城市:
泸州
================================================
----------------------今日天气-------------------
------------------------------------------------
城市 : 泸州
日期 : 2026-03-08
星期 : 星期日
天气 : 小雨转中雨
最高温度 :16℃
最低温度 :12℃
湿度 : 67%
风向 : 东北风
风力 : 2级
------------------------------------------------
----------------------未来天气-------------------
------------------------------------------------
日期 : 2026-03-09
星期 : 星期一
天气 : 小雨
最高温度 : 17℃
最低温度 : 10℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
------------------------------------------------
日期 : 2026-03-10
星期 : 星期二
天气 : 晴
最高温度 : 24℃
最低温度 : 11℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
------------------------------------------------
日期 : 2026-03-11
星期 : 星期三
天气 : 晴转多云
最高温度 : 25℃
最低温度 : 14℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
------------------------------------------------
日期 : 2026-03-12
星期 : 星期四
天气 : 多云转小雨
最高温度 : 25℃
最低温度 : 15℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
------------------------------------------------
日期 : 2026-03-13
星期 : 星期五
天气 : 阵雨转小雨
最高温度 : 24℃
最低温度 : 12℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
------------------------------------------------
日期 : 2026-03-14
星期 : 星期六
天气 : 小雨
最高温度 : 12℃
最低温度 : 10℃
风向 : 无持续风向
风力 : 小于3级
------------------------------------------------
================================================
6.可扩展方向
-
增加天气图标可视化
-
添加空气质量指数(AQI)查询
-
实现多语言支持
-
开发图形界面版本
📝 写在最后
这个小程序其实是我闲来无事自己折腾着玩写出来的,纯粹出于兴趣和对C语言、网络编程的一点学习热情。技术上肯定还有很多不成熟的地方,代码也可能写得比较"糙",把它分享出来,主要是想:
-
记录一下自己的学习过程,算是做个笔记。
-
和大家交流交流技术。如果你对C语言、网络请求、JSON解析这些感兴趣,或者觉得这个思路有点意思,欢迎一起探讨!
-
抛砖引玉。代码里肯定有不少可以改进、优化的地方,或者有更好的实现方式。非常欢迎大家提出宝贵的意见、建议或者批评! 相互学习,共同进步嘛。
所以,这完全是一个供学习交流的小玩意儿。如果你觉得它还有点用,或者能给你带来一点启发,那我就很开心了!当然,也欢迎你基于这个思路去折腾出更酷的东西!
欢迎交流,欢迎指正! 😊